fix(schema): align CR #7 declare≡execute constraints with gates

Update shortcut/app expectations, catalog jq, and schema-compat to accept
full hidden-sibling constraint groups, and bump delivered shortcut count to 216.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
玉澜
2026-08-03 16:37:48 +08:00
co-authored by Cursor
parent b1cefba808
commit 7a945318e0
4 changed files with 114 additions and 21 deletions
+10 -3
View File
@@ -161,14 +161,21 @@ if ! jq -e '
exit 1
fi
# declare≡execute may project hidden execute-side siblings into constraint
# groups. Require every group to touch at least one published parameter/positional;
# unpublished members are allowed as hidden companions.
if ! jq -e '
[.tools[] | select(.constraints != null)] as $tools |
($tools | length) >= 21 and
all($tools[];
(((.parameters // {}) | keys) + ((.positionals // []) | map(.name))) as $names |
all((.constraints.mutually_exclusive // [])[]; length > 1 and all(.[]; IN($names[]))) and
all((.constraints.require_one_of // [])[]; length > 1 and all(.[]; IN($names[]))) and
all((.constraints.require_together // [])[]; length > 1 and all(.[]; IN($names[])))
def ok_group:
length > 1 and
all(.[]; type == "string" and length > 0) and
any(.[]; IN($names[]));
all((.constraints.mutually_exclusive // [])[]; ok_group) and
all((.constraints.require_one_of // [])[]; ok_group) and
all((.constraints.require_together // [])[]; ok_group)
)
' "$catalog" >/dev/null; then
printf '%s\n' 'schema command constraints are incomplete or reference unknown parameters' >&2
+64 -1
View File
@@ -481,7 +481,6 @@ func checkToolCompatibility(toolPath string, oldTool, newTool toolSchema) []stri
{name: "interface_mode", old: oldTool.InterfaceMode, new: newTool.InterfaceMode},
{name: "interface_ref", old: oldTool.InterfaceRef, new: newTool.InterfaceRef},
{name: "availability", old: oldTool.Availability, new: newTool.Availability},
{name: "constraints", old: oldTool.Constraints, new: newTool.Constraints},
{name: "effect", old: oldTool.Effect, new: newTool.Effect},
{name: "risk", old: oldTool.Risk, new: newTool.Risk},
{name: "confirmation", old: oldTool.Confirmation, new: newTool.Confirmation},
@@ -491,6 +490,10 @@ func checkToolCompatibility(toolPath string, oldTool, newTool toolSchema) []stri
failures = append(failures, fmt.Sprintf("schema tool %q changed %s", toolPath, field.name))
}
}
if oldTool.Constraints != newTool.Constraints &&
!compatibleHiddenSiblingConstraintExpansion(oldTool, newTool) {
failures = append(failures, fmt.Sprintf("schema tool %q changed constraints", toolPath))
}
if !compatiblePositionals(oldTool.Positionals, newTool.Positionals) {
failures = append(failures, fmt.Sprintf("schema tool %q changed positionals", toolPath))
}
@@ -510,6 +513,66 @@ func checkToolCompatibility(toolPath string, oldTool, newTool toolSchema) []stri
return failures
}
// compatibleHiddenSiblingConstraintExpansion allows declare≡execute repairs:
// Schema may start projecting full constraint groups that include unpublished
// (hidden) execute-side siblings when the previous contract collapsed the sole
// published member to required and omitted constraints.
func compatibleHiddenSiblingConstraintExpansion(oldTool, newTool toolSchema) bool {
if strings.TrimSpace(oldTool.Constraints) != "" {
return false
}
var projected struct {
MutuallyExclusive [][]string `json:"mutually_exclusive"`
RequireOneOf [][]string `json:"require_one_of"`
RequireTogether [][]string `json:"require_together"`
}
if err := json.Unmarshal([]byte(newTool.Constraints), &projected); err != nil {
return false
}
if len(projected.RequireTogether) > 0 || len(projected.RequireOneOf) == 0 {
return false
}
groups := append([][]string(nil), projected.RequireOneOf...)
groups = append(groups, projected.MutuallyExclusive...)
for _, group := range groups {
if len(group) < 2 {
return false
}
published := 0
hidden := 0
for _, name := range group {
name = strings.TrimSpace(name)
if name == "" {
return false
}
if _, ok := newTool.Parameters[name]; ok {
published++
} else {
hidden++
}
}
if published == 0 || hidden == 0 {
return false
}
// Former collapse artifact: exactly one published member was required.
if published == 1 {
var sole string
for _, name := range group {
if _, ok := newTool.Parameters[name]; ok {
sole = name
break
}
}
oldParam, ok := oldTool.Parameters[sole]
newParam, okNew := newTool.Parameters[sole]
if !ok || !okNew || !oldParam.Required || newParam.Required {
return false
}
}
}
return true
}
func compatiblePositionals(oldPositionals, newPositionals []positionalSchema) bool {
if len(newPositionals) < len(oldPositionals) {
return false