Skip to content

Commit b734ad9

Browse files
author
SqlRush
committed
Validate schema numeric ranges
1 parent d3ba914 commit b734ad9

4 files changed

Lines changed: 32 additions & 1 deletion

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ M5 补充:WebSearch domain filters 现在在 schema 层声明 array `items:str
289289

290290
M5 补充:通用 tool schema validator 现在支持 `enum`,可直接执行 Grep output mode、NotebookEdit edit mode/cell type、Todo status/priority、Task target/action、LSP severity 等工具 schema 的枚举契约。
291291

292+
M5 补充:通用 tool schema validator 现在支持数字 `minimum`/`maximum`,可直接执行 LSPDiagnostics `limit` 等工具 schema 的数值范围契约。
293+
292294
M7 补充:scripted permission payload、dialog expectation、event、cancel-permission 和 dialog-result expectation 现在接受 `ID`/`ToolName`/`Actions``permissionID``requestID``toolUseID``operationID``operation``commandName``resourcePath``body``reasonText``allowedActions``buttons` 等相邻字段,并支持数字 request ID。
293295

294296
M6 补充:microcompact disk cache loader 和 prune 现在接受 digest 缺失但文件名已 keyed 的 cache entry,会用 `<digest>.json` 文件名作为 digest fallback,同时保留显式 digest mismatch 的 invalid-cache guard。

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ test/parity/ # golden tests against TS/official behavior
185185
- 本轮补充:WebSearch `query` schema 现在按官方 `min(2)` 约束拒绝单字符查询,通用 tool schema validator 同步支持 `minLength`,让工具定义可直接表达字符串最小长度契约。
186186
- 本轮补充:WebSearch domain filters 现在在 schema 层声明 array `items:string`,通用 tool schema validator 同步支持 `items` 校验;`allowed_domains`/`blocked_domains` 会拒绝空字符串、URL/port、非法 wildcard 和非域名 label。
187187
- 本轮补充:通用 tool schema validator 现在支持 `enum`,可直接执行 Grep output mode、NotebookEdit edit mode/cell type、Todo status/priority、Task target/action、LSP severity 等工具 schema 的枚举契约。
188+
- 本轮补充:通用 tool schema validator 现在支持数字 `minimum`/`maximum`,可直接执行 LSPDiagnostics `limit` 等工具 schema 的数值范围契约。
188189
- 本轮补充:WebFetch/WebSearch 输入解码现在兼容 `timeout``max_bytes`/`maxBytes``max_results`/`maxResults` 的 quoted semantic string 数值;WebSearch 也会按官方校验拒绝同一请求同时设置 `allowed_domains``blocked_domains`
189190
- 本轮补充:Grep 现在支持 whole-word 搜索参数 `word_regexp`/`wordRegexp`/`word-regexp`/`-w`,在 regex 和 fixed-string 模式下按词边界过滤匹配,并兼容 quoted boolean 输入。
190191
- 本轮补充:Grep 现在支持反向匹配参数 `invert_match`/`invertMatch`/`invert-match`/`-v``files_with_matches``content``count` 和 multiline 模式都会按非匹配行/未覆盖行输出,并兼容 quoted boolean 输入。

internal/tool/schema.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ func validateValue(schema contracts.JSONSchema, value any, path string) error {
3333
return fmt.Errorf("%s must be one of %s", path, describeEnumValues(enumValues))
3434
}
3535
}
36+
if minimum, ok := schemaNumberConstraint(schema["minimum"]); ok {
37+
if number, ok := schemaNumber(value); ok && number < minimum {
38+
return fmt.Errorf("%s must be at least %s", path, describeSchemaNumber(minimum))
39+
}
40+
}
41+
if maximum, ok := schemaNumberConstraint(schema["maximum"]); ok {
42+
if number, ok := schemaNumber(value); ok && number > maximum {
43+
return fmt.Errorf("%s must be at most %s", path, describeSchemaNumber(maximum))
44+
}
45+
}
3646
if minLength, ok := intSchemaConstraint(schema["minLength"]); ok {
3747
text, ok := value.(string)
3848
if ok && utf8.RuneCountInString(text) < minLength {
@@ -194,6 +204,17 @@ func schemaNumber(value any) (float64, bool) {
194204
}
195205
}
196206

207+
func schemaNumberConstraint(value any) (float64, bool) {
208+
return schemaNumber(value)
209+
}
210+
211+
func describeSchemaNumber(value float64) string {
212+
if math.Trunc(value) == value {
213+
return fmt.Sprintf("%.0f", value)
214+
}
215+
return fmt.Sprintf("%g", value)
216+
}
217+
197218
func intSchemaConstraint(value any) (int, bool) {
198219
switch v := value.(type) {
199220
case int:

internal/tool/tool_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func TestValidateSchema(t *testing.T) {
3939
"path": map[string]any{"type": "string", "minLength": 2},
4040
"mode": map[string]any{"type": "string", "enum": []any{"read", "write"}},
4141
"count": map[string]any{"type": "integer", "enum": []any{1, 2}},
42+
"limit": map[string]any{"type": "integer", "minimum": 1, "maximum": 5},
4243
"tags": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
4344
},
4445
}
@@ -57,10 +58,16 @@ func TestValidateSchema(t *testing.T) {
5758
if err := ValidateSchema(schema, json.RawMessage(`{"path":"README.md","count":3}`)); err == nil || !strings.Contains(err.Error(), "input.count must be one of 1, 2") {
5859
t.Fatalf("err = %v", err)
5960
}
61+
if err := ValidateSchema(schema, json.RawMessage(`{"path":"README.md","limit":0}`)); err == nil || !strings.Contains(err.Error(), "input.limit must be at least 1") {
62+
t.Fatalf("err = %v", err)
63+
}
64+
if err := ValidateSchema(schema, json.RawMessage(`{"path":"README.md","limit":6}`)); err == nil || !strings.Contains(err.Error(), "input.limit must be at most 5") {
65+
t.Fatalf("err = %v", err)
66+
}
6067
if err := ValidateSchema(schema, json.RawMessage(`{"path":"README.md"}`)); err != nil {
6168
t.Fatal(err)
6269
}
63-
if err := ValidateSchema(schema, json.RawMessage(`{"path":"README.md","mode":"read","count":2}`)); err != nil {
70+
if err := ValidateSchema(schema, json.RawMessage(`{"path":"README.md","mode":"read","count":2,"limit":5}`)); err != nil {
6471
t.Fatal(err)
6572
}
6673
}

0 commit comments

Comments
 (0)