Files
dingtalk-workspace-cli/internal/cli/contract_final.go
T
玉澜 e705012011 feat(schema): migrate parameter overlays into code declarations
Move 121/210 parameter-level Schema field overlays from
schema_hints/metadata JSON into in-code ParamDecl declarations on
DeclareLeafMetadata commands. The declared values are emitted as
dws.schema.* annotations at assembly time via ApplyParamDecls,
outranking tool_schema_hint (rank 620 > 500) so the hint overlay
becomes redundant once the declaration is in place.

Key mechanism changes:
- Add SchemaDecl.Parameters []ParamDecl with property/required/
  interface_type/description/required_when/enum fields
- Add cli.ParamDecl type carried inside ContractFinalPayload
- ApplyParamDecls emits dws.schema.* annotations from the payload
  at assembly time (no sync.Map, no tree-rebuild key issue)
- Add cli.AnnotateRuntimeFlagInterfaceType (41 overlays needed it)
- Add cli.AnnotateRuntimeFlagRequiredValue for explicit true/false
- Compatibility alias check tolerates dws.schema.* annotation
  differences between primary and alias commands
- Remove runtime source_hash recomputation (87ms/997k allocs saved;
  enforced by check-generated-drift.sh at build time instead)
- Add shortcut.Flag.RequiredWhen and wire through FromShortcut
- Add boolFlag OR semantics to fix confirmationBypass disagreement
- Add bindKey default kebab-to-camel for forgotten Bind
- Add schema consumption benchmarks (catalog decode 1.5s/817MB,
  shortcut load 1.9ms/3MB — three orders of magnitude apart)
- Add catalog codegen feasibility probe (34 tools: 0.06s compile,
  31ns access, 87KB linked — extrapolates to 2.1MB for 845 tools)

Migrated products (29 tools, 121 fields):
  aisearch(1), chat(12), contact(4), doc(3), drive(1),
  hrbrain(10), mail(2), report(1), sheet(3), todo(6)

Remaining 89 fields across 11 products blocked by:
  - RPCName not found as string literal (19 tools, shortcut/variable)
  - No matching DeclareLeafMetadata near callMCPTool (11 tools)
  - No single RPCName for multi-step commands (drive.upload etc.)

All tests green: corecmd, cli, helpers. Generation and drift clean.
2026-08-01 09:25:40 +08:00

129 lines
3.9 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"strings"
"sync"
"github.com/spf13/cobra"
)
// ContractFinalPayload is the Contract-authored final Schema leaf overlay.
// Registered in-process by the framework; Schema assembly reads it as
// pass-through. No JSON bridge. Treat as read-only after Register.
type ContractFinalPayload struct {
Title string
Description string
Positionals []RuntimeSchemaPositional
Parameters []ParamDecl
Safety *SafetySpec
DryRun *DryRunSpec
Interface *InterfaceSpec
Selection *SelectionSpec
Identity *ToolIdentitySpec
}
var contractFinalByCommand sync.Map // *cobra.Command → *ContractFinalPayload
// ParamDecl is one parameter-level Schema fact declared on a command. It is
// stored at DeclareLeafMetadata time and applied as annotations at assembly
// time, when all flags are guaranteed to exist on the fully-built command tree.
type ParamDecl struct {
Name string
Property string
Required *bool
InterfaceType string
Description string
RequiredWhen string
Enum []string
}
// ApplyParamDecls emits parameter declarations as dws.schema.* annotations on the
// command's flags. Called at assembly time (runtimeToolSpecFromContractFinal)
// when all flags exist on the fully-built command tree. The decls come from
// the ContractFinalPayload, so no separate storage is needed.
func ApplyParamDecls(cmd *cobra.Command, decls []ParamDecl) {
if cmd == nil || len(decls) == 0 {
return
}
for _, p := range decls {
name := strings.TrimSpace(p.Name)
if name == "" {
continue
}
if prop := strings.TrimSpace(p.Property); prop != "" {
AnnotateRuntimeFlagProperty(cmd, name, prop)
}
if p.Required != nil {
AnnotateRuntimeFlagRequiredValue(cmd, name, *p.Required)
}
if it := strings.TrimSpace(p.InterfaceType); it != "" {
AnnotateRuntimeFlagInterfaceType(cmd, name, it)
}
if desc := strings.TrimSpace(p.Description); desc != "" {
AnnotateRuntimeFlagDescription(cmd, name, desc)
}
if rw := strings.TrimSpace(p.RequiredWhen); rw != "" {
AnnotateRuntimeFlagRequiredWhen(cmd, name, rw)
}
if len(p.Enum) > 0 {
AnnotateRuntimeFlagEnum(cmd, name, p.Enum...)
}
}
}
// RegisterRuntimeContractFinal stores the typed final Schema overlay for a leaf.
// Light runtime write: one map store, no JSON, no deep clone.
func RegisterRuntimeContractFinal(cmd *cobra.Command, payload ContractFinalPayload) {
if cmd == nil {
return
}
AnnotateRuntimeContract(cmd)
p := payload
contractFinalByCommand.Store(cmd, &p)
}
// RuntimeContractFinal returns the registered final Schema overlay (read-only).
func RuntimeContractFinal(cmd *cobra.Command) (ContractFinalPayload, bool) {
if cmd == nil {
return ContractFinalPayload{}, false
}
raw, ok := contractFinalByCommand.Load(cmd)
if !ok {
return ContractFinalPayload{}, false
}
p, ok := raw.(*ContractFinalPayload)
if !ok || p == nil {
return ContractFinalPayload{}, false
}
return *p, true
}
// HasRuntimeContractFinal reports whether the leaf has a registered final overlay.
func HasRuntimeContractFinal(cmd *cobra.Command) bool {
if cmd == nil {
return false
}
_, ok := contractFinalByCommand.Load(cmd)
return ok
}
// ClearRuntimeContractFinalForTest removes a registration (tests only).
func ClearRuntimeContractFinalForTest(cmd *cobra.Command) {
if cmd != nil {
contractFinalByCommand.Delete(cmd)
}
}