Files
玉澜 2e51dcf35d docs(schema): tell the truth about single-source identity and wire policy
- schema_cobra_binding: the Identity-vs-spec check is now a defensive
  self-consistency assertion (the spec is collected from the same
  ContractFinal.Identity), not a cross-source pin; name the real drift
  anchors (native annotation cross-check, collector uniqueness
  self-validation, homology tool-count tripwire, surface/catalog hash
  baselines) and relabel the mismatch diagnostic as collected vs declared.
- schema-compat: state explicitly that accepting interface_type clearing
  is a deliberate wire-visible policy decision taken with the MCP pin
  retirement (missing = unknown; re-population requires ParamDecl).
- schema_command_registry: drop retired bindings audit / MCP pin from the
  peer reviewed-inputs comment; note they must not reappear.
- canonical: schema help no longer claims commands must enter a reviewed
  registry; identity is collected from ContractFinal.Identity.
- homology: the tool-count tripwire error now says where to bump it after
  review.
- Sweep stale 'reviewed registry' wording in corecmd and the help-flag
  completeness gate comment; AGENTS.md interface-facts section matches.
2026-08-03 19:22:53 +08:00

244 lines
9.7 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package cli
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"regexp"
"sort"
"strings"
"github.com/spf13/cobra"
)
// Command identity is collected from the live Cobra command tree: every
// runnable leaf carrying ContractFinal.Identity contributes one CommandSpec
// (CollectIdentitySpecs in schema_identity_collect.go). The reviewed
// schema_command_registry/ was retired together with that switchover; the
// collector is the single source of stable command identity and navigation.
// Catalog and generated metadata are downstream views and must never be read
// back here. Peer reviewed inputs (param_concepts, exclusions, mapping
// ledger) stay separate — see AGENTS.md "Reviewed inputs". The bindings
// audit JSON and the MCP metadata/service-review pins are retired and must
// not reappear.
var (
commandRegistryProductIDPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*$`)
commandRegistryCanonicalPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*\.[A-Za-z0-9][A-Za-z0-9_.-]*$`)
commandRegistryCLIPathToken = regexp.MustCompile(`^(?:[A-Za-z0-9][A-Za-z0-9._:-]*|\+[A-Za-z0-9][A-Za-z0-9._:-]*)$`)
)
// CommandSourceContractIdentity is the delivery source label for command
// identity collected from ContractFinal.Identity declarations. The collector
// (CollectIdentitySpecs) is the single identity source since the reviewed
// schema_command_registry retirement.
const CommandSourceContractIdentity = "contract_identity"
// CommandSpec is one command identity. Identity and navigation are
// deliberately kept together so no downstream renderer can independently
// invent a canonical name, primary path, or alias.
type CommandSpec struct {
CanonicalPath string
SourceProductID string
PrimaryCLIPath string
Aliases []string
Visibility SchemaVisibility
Source string
ReviewReason string
}
// CommandRegistry is an indexed command identity set.
type CommandRegistry struct {
Commands []CommandSpec
ByCLIPath map[string]CommandSpec
ByCanonical map[string]CommandSpec
}
// EffectiveCommandRegistry is the collected command identity registry after
// indexing. It remains independent of Cobra; binding is a separate
// fail-closed step.
type EffectiveCommandRegistry struct {
Commands []CommandSpec
ByCLIPath map[string]CommandSpec
ByCanonical map[string]CommandSpec
}
// SourceHash hashes only stable identity, navigation, and reviewed exposure.
// Formatting, product order, provenance labels, and omitted default
// source_product_id/visibility values do not affect it.
func (registry CommandRegistry) SourceHash() string {
return hashCommandSpecs(registry.Commands)
}
// SourceHash covers every effective command identity delivered to downstream
// consumers.
func (registry EffectiveCommandRegistry) SourceHash() string {
return hashCommandSpecs(registry.Commands)
}
func hashCommandSpecs(commands []CommandSpec) string {
rows := make([]string, 0, len(commands))
for _, spec := range commands {
productID, _, _ := splitManualSchemaCanonicalPath(spec.CanonicalPath)
sourceProductID := strings.TrimSpace(spec.SourceProductID)
if sourceProductID == productID {
sourceProductID = ""
}
aliases := normalizeCommandAliases(spec.Aliases, normalizeSchemaCLIPath(spec.PrimaryCLIPath))
visibility := spec.Visibility
if visibility == "" {
visibility = SchemaVisibilityPublic
}
rows = append(rows, productID+"\x00"+strings.TrimSpace(spec.CanonicalPath)+"\x00"+sourceProductID+"\x00"+normalizeSchemaCLIPath(spec.PrimaryCLIPath)+"\x00"+strings.Join(aliases, "\x00")+"\x00"+string(visibility))
}
sort.Strings(rows)
sum := sha256.Sum256([]byte(strings.Join(rows, "\n")))
return "sha256:" + hex.EncodeToString(sum[:])
}
func newEffectiveCommandRegistry(commands []CommandSpec) (EffectiveCommandRegistry, error) {
normalized, byPath, byCanonical, err := indexCommandSpecs(commands)
if err != nil {
return EffectiveCommandRegistry{}, err
}
return EffectiveCommandRegistry{Commands: normalized, ByCLIPath: byPath, ByCanonical: byCanonical}, nil
}
func indexCommandSpecs(commands []CommandSpec) ([]CommandSpec, map[string]CommandSpec, map[string]CommandSpec, error) {
normalized := make([]CommandSpec, 0, len(commands))
byPath := make(map[string]CommandSpec, len(commands))
byCanonical := make(map[string]CommandSpec, len(commands))
for _, raw := range commands {
spec := cloneCommandSpec(raw)
spec.CanonicalPath = strings.TrimSpace(spec.CanonicalPath)
productID, _, ok := splitManualSchemaCanonicalPath(spec.CanonicalPath)
if !ok || !commandRegistryCanonicalPattern.MatchString(spec.CanonicalPath) {
return nil, nil, nil, fmt.Errorf("command identity has invalid canonical path %q", raw.CanonicalPath)
}
spec.SourceProductID = strings.TrimSpace(spec.SourceProductID)
if spec.SourceProductID == "" {
spec.SourceProductID = productID
}
if !validCommandRegistryProductID(spec.SourceProductID) {
return nil, nil, nil, fmt.Errorf("command identity %s has invalid source_product_id %q", spec.CanonicalPath, raw.SourceProductID)
}
spec.PrimaryCLIPath = normalizeSchemaCLIPath(spec.PrimaryCLIPath)
if !validCommandRegistryCLIPath(spec.PrimaryCLIPath) {
return nil, nil, nil, fmt.Errorf("command identity %s has invalid primary cli path %q", spec.CanonicalPath, raw.PrimaryCLIPath)
}
aliases := make([]string, 0, len(spec.Aliases))
seenAliases := make(map[string]bool, len(spec.Aliases))
for _, rawAlias := range spec.Aliases {
alias := normalizeSchemaCLIPath(rawAlias)
if !validCommandRegistryCLIPath(alias) {
return nil, nil, nil, fmt.Errorf("command identity %s has invalid alias path %q", spec.CanonicalPath, alias)
}
if alias == spec.PrimaryCLIPath {
return nil, nil, nil, fmt.Errorf("command identity %s alias %q duplicates its primary path", spec.CanonicalPath, alias)
}
if seenAliases[alias] {
return nil, nil, nil, fmt.Errorf("command identity %s has duplicate alias path %q", spec.CanonicalPath, alias)
}
seenAliases[alias] = true
aliases = append(aliases, alias)
}
spec.Aliases = sortedUniqueStrings(aliases)
if spec.Visibility == "" {
spec.Visibility = SchemaVisibilityPublic
}
switch spec.Visibility {
case SchemaVisibilityPublic, SchemaVisibilityCompat, SchemaVisibilityInternal:
default:
return nil, nil, nil, fmt.Errorf("command identity %s has invalid visibility %q", spec.CanonicalPath, spec.Visibility)
}
spec.Source = strings.TrimSpace(spec.Source)
if spec.Source == "" {
spec.Source = CommandSourceContractIdentity
}
spec.ReviewReason = strings.TrimSpace(spec.ReviewReason)
if previous, exists := byCanonical[spec.CanonicalPath]; exists {
return nil, nil, nil, fmt.Errorf("duplicate command identity canonical path %s (primary paths %q and %q)", spec.CanonicalPath, previous.PrimaryCLIPath, spec.PrimaryCLIPath)
}
byCanonical[spec.CanonicalPath] = spec
for _, path := range append([]string{spec.PrimaryCLIPath}, spec.Aliases...) {
if previous, exists := byPath[path]; exists {
return nil, nil, nil, fmt.Errorf("command identity path %q belongs to both %s and %s", path, previous.CanonicalPath, spec.CanonicalPath)
}
byPath[path] = spec
}
normalized = append(normalized, spec)
}
for path, owner := range byPath {
if canonicalOwner, exists := byCanonical[path]; exists {
return nil, nil, nil, fmt.Errorf(
"command identity CLI path %q for %s conflicts with canonical identity %s",
path,
owner.CanonicalPath,
canonicalOwner.CanonicalPath,
)
}
}
sort.Slice(normalized, func(i, j int) bool { return normalized[i].CanonicalPath < normalized[j].CanonicalPath })
return normalized, byPath, byCanonical, nil
}
func validCommandRegistryProductID(value string) bool {
return commandRegistryProductIDPattern.MatchString(strings.TrimSpace(value))
}
// validCommandRegistryCLIPath is intentionally stricter than
// normalizeSchemaCLIPath: collected identity must already be canonical and
// may not rely on normalization to hide a leading dws, repeated whitespace,
// flags, or wildcard syntax.
func validCommandRegistryCLIPath(value string) bool {
if value == "" || value != strings.TrimSpace(value) || strings.HasPrefix(value, "dws ") || strings.ContainsAny(value, "*?[]") {
return false
}
parts := strings.Split(value, " ")
for _, part := range parts {
if !commandRegistryCLIPathToken.MatchString(part) {
return false
}
}
return true
}
func normalizeCommandAliases(aliases []string, primary string) []string {
normalized := make([]string, 0, len(aliases))
for _, alias := range aliases {
alias = normalizeSchemaCLIPath(alias)
if alias != "" && alias != primary {
normalized = append(normalized, alias)
}
}
return sortedUniqueStrings(normalized)
}
// BuildEffectiveCommandRegistry assembles the effective command identity
// registry by collecting ContractFinal.Identity from the live Cobra leaves
// under root. The collector is the single identity source; there is no
// separate reviewed identity file to merge or overlay.
//
// Parameter mapping ledger (schema_parameter_mapping_ledger.go —
// mapping_exclusions / removals; active bindings retired to ParamDecl.Property)
// is validated at BindEffectiveCommandRegistry and catalog assembly, not here.
// Identity registry construction must not hard-depend on active binding rows.
func BuildEffectiveCommandRegistry(root *cobra.Command) (EffectiveCommandRegistry, error) {
if root == nil {
return EffectiveCommandRegistry{}, fmt.Errorf("build effective Schema command registry: root is nil")
}
collected, _, err := CollectIdentitySpecs(root)
if err != nil {
return EffectiveCommandRegistry{}, err
}
return newEffectiveCommandRegistry(collected)
}
func cloneCommandSpec(spec CommandSpec) CommandSpec {
spec.Aliases = append([]string(nil), spec.Aliases...)
return spec
}