Revert "declare(ding): semantic ParamDecl backfill for schema inference residuals"
This reverts commit 969292a8d7.
This commit is contained in:
1 parent
a1f8ecb7f0
commit
0353215b1d
5 files changed
+6
-261
No files matched your search
@@ -1416,57 +1416,3 @@ func TestDeliveryCatalogChatParamDeclsFrom87910880Reviewed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeliveryCatalogDingSemanticParamDeclsSpotCheck locks the ding pilot
|
||||
// ParamDecl backfill: Execute/CallMCP keys win over flag-name inference, and
|
||||
// required stays declare/cobra-backed rather than usage_required_inference.
|
||||
func TestDeliveryCatalogDingSemanticParamDeclsSpotCheck(t *testing.T) {
|
||||
cases := []struct {
|
||||
path string
|
||||
flag string
|
||||
property string
|
||||
required bool
|
||||
}{
|
||||
{"ding +list", "cursor", "cursor", false},
|
||||
{"ding +list", "type", "type", false},
|
||||
{"ding +receiver-status", "ding-id", "openDingId", true},
|
||||
{"ding +recall-personal", "id", "openDingId", true},
|
||||
{"ding +send-personal", "users", "receiverOpenDingTalkIds", true},
|
||||
{"ding +send-personal", "content", "content", true},
|
||||
{"ding +send-personal", "type", "remindType", false},
|
||||
{"ding +send-personal", "uuid", "uuid", false},
|
||||
{"ding message send", "content", "content", true},
|
||||
{"ding message send", "robot-code", "robotCode", true},
|
||||
{"ding message send", "type", "remindType", false},
|
||||
{"ding message send", "users", "receiverUserIdList", true},
|
||||
{"ding message recall", "id", "openDingId", true},
|
||||
{"ding message recall", "robot-code", "robotCode", true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
leaf, err := queryDeliverySchemaPayload([]string{tc.path})
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", tc.path, err)
|
||||
}
|
||||
param := schemaMap(leaf["parameters"])[tc.flag]
|
||||
if param == nil {
|
||||
t.Fatalf("%s missing --%s", tc.path, tc.flag)
|
||||
}
|
||||
if got, _ := param["property"].(string); got != tc.property {
|
||||
t.Fatalf("%s --%s property = %q, want %q", tc.path, tc.flag, got, tc.property)
|
||||
}
|
||||
propProv := schemaMap(param["field_provenance"])["property"]
|
||||
if src, _ := propProv["source"].(string); src == "flag_name_inference" {
|
||||
t.Fatalf("%s --%s property still flag_name_inference", tc.path, tc.flag)
|
||||
}
|
||||
gotRequired, _ := param["required"].(bool)
|
||||
if gotRequired != tc.required {
|
||||
t.Fatalf("%s --%s required = %v, want %v", tc.path, tc.flag, gotRequired, tc.required)
|
||||
}
|
||||
if tc.required {
|
||||
reqProv := schemaMap(param["field_provenance"])["required"]
|
||||
if src, _ := reqProv["source"].(string); src == "usage_required_inference" {
|
||||
t.Fatalf("%s --%s required still usage_required_inference", tc.path, tc.flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,8 +115,8 @@ func newDingCommand() *cobra.Command {
|
||||
},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "content", Property: "content", Required: boolPtr(true)},
|
||||
{Name: "robot-code", Property: "robotCode", Required: boolPtr(true)},
|
||||
{Name: "content", Required: boolPtr(true)},
|
||||
{Name: "robot-code", Required: boolPtr(true)},
|
||||
{Name: "type", Property: "remindType"},
|
||||
{Name: "users", Property: "receiverUserIdList", Required: boolPtr(true), InterfaceType: "array"},
|
||||
},
|
||||
@@ -170,10 +170,7 @@ func newDingCommand() *cobra.Command {
|
||||
Examples: []string{"dws ding message recall --robot-code <ROBOT_CODE> --id <OPEN_DING_ID> --format json"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
// id is validateRequiredFlags-hard; robot-code is required unless
|
||||
// DINGTALK_DING_ROBOT_CODE is set (same Agent-facing contract as send).
|
||||
{Name: "id", Property: "openDingId", Required: boolPtr(true)},
|
||||
{Name: "robot-code", Property: "robotCode", Required: boolPtr(true)},
|
||||
{Name: "id", Property: "openDingId"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -49,12 +49,6 @@ var List = shortcut.Shortcut{
|
||||
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
||||
Examples: []string{"dws ding +list"},
|
||||
},
|
||||
// Property matches CallMCP keys in Execute; type stays optional because
|
||||
// the CLI default ALL satisfies the server non-empty requirement.
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "cursor", Property: "cursor"},
|
||||
{Name: "type", Property: "type"},
|
||||
},
|
||||
},
|
||||
Flags: []shortcut.Flag{
|
||||
{Name: "cursor", Type: shortcut.FlagInt, Desc: "分页游标 (可选)"},
|
||||
@@ -101,9 +95,6 @@ var ReceiverStatus = shortcut.Shortcut{
|
||||
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
||||
Examples: []string{"dws ding +receiver-status --ding-id <DING_ID>"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "ding-id", Property: "openDingId", Required: boolPtr(true)},
|
||||
},
|
||||
},
|
||||
Flags: []shortcut.Flag{
|
||||
{Name: "ding-id", Type: shortcut.FlagString, Desc: "openDingId", Required: true},
|
||||
@@ -142,13 +133,6 @@ var SendPersonal = shortcut.Shortcut{
|
||||
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
||||
Examples: []string{"dws ding +send-personal --users <VALUES> --content <CONTENT>"},
|
||||
},
|
||||
// Property keys match send_personal_ding toolArgs (not flag-name camelCase).
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "users", Property: "receiverOpenDingTalkIds", Required: boolPtr(true)},
|
||||
{Name: "content", Property: "content", Required: boolPtr(true)},
|
||||
{Name: "type", Property: "remindType"},
|
||||
{Name: "uuid", Property: "uuid"},
|
||||
},
|
||||
},
|
||||
Flags: []shortcut.Flag{
|
||||
{Name: "users", Type: shortcut.FlagStringSlice, Desc: "接收人 openDingTalkId 列表 (CSV)", Required: true},
|
||||
@@ -220,9 +204,6 @@ var RecallPersonal = shortcut.Shortcut{
|
||||
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
|
||||
Examples: []string{"dws ding +recall-personal --id <ID>"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "id", Property: "openDingId", Required: boolPtr(true)},
|
||||
},
|
||||
},
|
||||
Flags: []shortcut.Flag{
|
||||
{Name: "id", Type: shortcut.FlagString, Desc: "openDingId", Required: true},
|
||||
@@ -234,8 +215,6 @@ var RecallPersonal = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool { return &v }
|
||||
|
||||
func init() {
|
||||
shortcut.Register(
|
||||
List,
|
||||
|
||||
@@ -730,6 +730,7 @@ func checkParameterCompatibility(toolPath, name string, oldParameter, newParamet
|
||||
new string
|
||||
}{
|
||||
{name: "type", old: oldParameter.Type, new: newParameter.Type},
|
||||
{name: "property", old: oldParameter.Property, new: newParameter.Property},
|
||||
{name: "default", old: oldParameter.Default, new: newParameter.Default},
|
||||
{name: "interface_default", old: oldParameter.InterfaceDefault, new: newParameter.InterfaceDefault},
|
||||
{name: "format", old: oldParameter.Format, new: newParameter.Format},
|
||||
@@ -738,13 +739,6 @@ func checkParameterCompatibility(toolPath, name string, oldParameter, newParamet
|
||||
failures = append(failures, fmt.Sprintf("schema tool %q parameter %q changed %s", toolPath, name, field.name))
|
||||
}
|
||||
}
|
||||
// Property remaps are incompatible unless a reviewed correction replaces
|
||||
// flag-name inference with the ParamDecl-declared MCP field. Arbitrary
|
||||
// non-empty A→B remaps remain a contract break.
|
||||
if oldParameter.Property != newParameter.Property &&
|
||||
!compatibleReviewedPropertyCorrection(toolPath, name, oldParameter.Property, newParameter.Property) {
|
||||
failures = append(failures, fmt.Sprintf("schema tool %q parameter %q changed property", toolPath, name))
|
||||
}
|
||||
// Clearing interface_type is accepted as compatible: a deliberate,
|
||||
// wire-visible policy decision taken with the pinned MCP metadata
|
||||
// retirement. Production no longer projects MCP-sourced types unless
|
||||
@@ -789,61 +783,6 @@ func enumNarrowed(oldValues, newValues []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// reviewedPropertyCorrection is an exact allowlist entry for replacing a
|
||||
// historical wire property (typically flag-name inference) with the MCP field
|
||||
// declared on leaf ParamDecl.Property. Do not use this to bless arbitrary
|
||||
// remaps: each entry must name tool path, parameter, old property, and new
|
||||
// property exactly.
|
||||
type reviewedPropertyCorrection struct {
|
||||
toolPath string
|
||||
param string
|
||||
oldProperty string
|
||||
newProperty string
|
||||
}
|
||||
|
||||
// Keep this list tiny and evidence-backed. Prefer ParamDecl declaration; only
|
||||
// add an entry when schema-compat would otherwise freeze a wrong inferred
|
||||
// property that agents bind against. ding pilot: Execute/CallMCP keys are the
|
||||
// ground truth (see internal/shortcut/ding/ding.go).
|
||||
var reviewedPropertyCorrections = []reviewedPropertyCorrection{
|
||||
{
|
||||
toolPath: "ding/ding.shortcut_receiver_status",
|
||||
param: "ding-id",
|
||||
oldProperty: "dingId",
|
||||
newProperty: "openDingId",
|
||||
},
|
||||
{
|
||||
toolPath: "ding/ding.shortcut_recall_personal",
|
||||
param: "id",
|
||||
oldProperty: "id",
|
||||
newProperty: "openDingId",
|
||||
},
|
||||
{
|
||||
toolPath: "ding/ding.shortcut_send_personal",
|
||||
param: "users",
|
||||
oldProperty: "users",
|
||||
newProperty: "receiverOpenDingTalkIds",
|
||||
},
|
||||
{
|
||||
toolPath: "ding/ding.shortcut_send_personal",
|
||||
param: "type",
|
||||
oldProperty: "type",
|
||||
newProperty: "remindType",
|
||||
},
|
||||
}
|
||||
|
||||
func compatibleReviewedPropertyCorrection(toolPath, param, oldProperty, newProperty string) bool {
|
||||
for _, entry := range reviewedPropertyCorrections {
|
||||
if entry.toolPath == toolPath &&
|
||||
entry.param == param &&
|
||||
entry.oldProperty == oldProperty &&
|
||||
entry.newProperty == newProperty {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func mergeContracts(historical, current schemaContract) (schemaContract, []string) {
|
||||
failures := checkCompatibility(historical, current)
|
||||
if len(failures) > 0 {
|
||||
|
||||
@@ -595,129 +595,13 @@ func TestCrossPlatformCoverageSchemaCompatMCPRetirementAndConstraintExpansion(t
|
||||
t.Fatalf("non-empty interface_type change should fail: %v", failures)
|
||||
}
|
||||
|
||||
// Reviewed ding property remaps must pass; arbitrary remaps must fail.
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_send_personal", "users", "users", "receiverOpenDingTalkIds",
|
||||
) {
|
||||
t.Fatal("reviewed ding +send-personal --users correction must be accepted")
|
||||
}
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_send_personal", "type", "type", "remindType",
|
||||
) {
|
||||
t.Fatal("reviewed ding +send-personal --type correction must be accepted")
|
||||
}
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_receiver_status", "ding-id", "dingId", "openDingId",
|
||||
) {
|
||||
t.Fatal("reviewed ding +receiver-status --ding-id correction must be accepted")
|
||||
}
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_recall_personal", "id", "id", "openDingId",
|
||||
) {
|
||||
t.Fatal("reviewed ding +recall-personal --id correction must be accepted")
|
||||
}
|
||||
if compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_send_personal", "users", "users", "userIds",
|
||||
) {
|
||||
t.Fatal("non-reviewed new property must remain incompatible")
|
||||
}
|
||||
if compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.send_ding_message", "users", "users", "receiverUserIdList",
|
||||
) {
|
||||
t.Fatal("unlisted tool/param remap must remain incompatible")
|
||||
}
|
||||
// Property remaps remain a hard contract break (no reviewed allowlist).
|
||||
propertyRemap := cloneContract(baseline)
|
||||
mutateParameter(&propertyRemap, func(parameter *parameterSchema) {
|
||||
parameter.Property = "unreviewedRemoteField"
|
||||
})
|
||||
if failures := checkCompatibility(baseline, propertyRemap); !strings.Contains(strings.Join(failures, "\n"), "changed property") {
|
||||
t.Fatalf("unreviewed property remap should fail: %v", failures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageReviewedDingPropertyCorrections(t *testing.T) {
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_send_personal", "users", "users", "receiverOpenDingTalkIds",
|
||||
) {
|
||||
t.Fatal("reviewed ding +send-personal --users correction must be accepted")
|
||||
}
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_send_personal", "type", "type", "remindType",
|
||||
) {
|
||||
t.Fatal("reviewed ding +send-personal --type correction must be accepted")
|
||||
}
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_receiver_status", "ding-id", "dingId", "openDingId",
|
||||
) {
|
||||
t.Fatal("reviewed ding +receiver-status --ding-id correction must be accepted")
|
||||
}
|
||||
if !compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_recall_personal", "id", "id", "openDingId",
|
||||
) {
|
||||
t.Fatal("reviewed ding +recall-personal --id correction must be accepted")
|
||||
}
|
||||
if compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.shortcut_send_personal", "users", "users", "userIds",
|
||||
) {
|
||||
t.Fatal("non-reviewed new property must remain incompatible")
|
||||
}
|
||||
if compatibleReviewedPropertyCorrection(
|
||||
"ding/ding.send_ding_message", "users", "users", "receiverUserIdList",
|
||||
) {
|
||||
t.Fatal("unlisted tool/param remap must remain incompatible")
|
||||
}
|
||||
|
||||
baseline := schemaContract{
|
||||
Products: map[string]productSchema{
|
||||
"ding": {Tools: map[string]toolSchema{
|
||||
"ding.shortcut_send_personal": {
|
||||
PrimaryCLIPath: "ding +send-personal",
|
||||
InterfaceMode: "composite",
|
||||
Availability: "available",
|
||||
Effect: "write",
|
||||
Risk: "medium",
|
||||
Confirmation: "user_required",
|
||||
Idempotency: "unknown",
|
||||
Parameters: map[string]parameterSchema{
|
||||
"users": {Type: "array", Property: "users"},
|
||||
"type": {Type: "string", Property: "type"},
|
||||
},
|
||||
},
|
||||
"ding.shortcut_receiver_status": {
|
||||
PrimaryCLIPath: "ding +receiver-status",
|
||||
InterfaceMode: "composite",
|
||||
Availability: "available",
|
||||
Effect: "read",
|
||||
Risk: "low",
|
||||
Confirmation: "not_required",
|
||||
Idempotency: "idempotent",
|
||||
Parameters: map[string]parameterSchema{
|
||||
"ding-id": {Type: "string", Property: "dingId", Required: true},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
corrected := cloneContract(baseline)
|
||||
corrected.Products["ding"].Tools["ding.shortcut_send_personal"].Parameters["users"] = parameterSchema{
|
||||
Type: "array", Property: "receiverOpenDingTalkIds",
|
||||
}
|
||||
corrected.Products["ding"].Tools["ding.shortcut_send_personal"].Parameters["type"] = parameterSchema{
|
||||
Type: "string", Property: "remindType",
|
||||
}
|
||||
corrected.Products["ding"].Tools["ding.shortcut_receiver_status"].Parameters["ding-id"] = parameterSchema{
|
||||
Type: "string", Property: "openDingId", Required: true,
|
||||
}
|
||||
if failures := checkCompatibility(baseline, corrected); len(failures) != 0 {
|
||||
t.Fatalf("reviewed ding property corrections should pass: %v", failures)
|
||||
}
|
||||
|
||||
broken := cloneContract(baseline)
|
||||
broken.Products["ding"].Tools["ding.shortcut_send_personal"].Parameters["users"] = parameterSchema{
|
||||
Type: "array", Property: "userIds",
|
||||
}
|
||||
if failures := checkCompatibility(baseline, broken); !strings.Contains(strings.Join(failures, "\n"), "changed property") {
|
||||
t.Fatalf("unreviewed property remap should fail: %v", failures)
|
||||
t.Fatalf("property remap should fail: %v", failures)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user