Skip to content

Migrate sync-versions.sh → wfctl registry sync subcommand (consolidates Layer 3b + deferred I3 capability gate) #762

@intel352

Description

@intel352

Context

workflow-registry/scripts/sync-versions.sh is bash + jq + gh CLI. workflow#758 added a strict-semver tag gate to it (PR #110, 940ecc405c) — but that regex is now duplicated with wfctl plugin validate-contract --for-publish (Layer 1). Two implementations of the same rule = drift risk.

Cycle-4 plan adversarial review flagged this as "tooling decision creep" (I1 in workflow-registry context); it was accepted pragmatically at the time to keep Layer 2 scoped down. Coming back to address it now.

Problems with the current bash sync

  1. Regex duplication between sync-versions.sh and wfctl plugin validate-contract --for-publish. Both define ^v\d+\.\d+\.\d+$. Future changes (e.g. allowing -rc.N once ParseSemver supports it) require touching two places.
  2. No test harness in workflow-registry repo. Layer 2 PR shipped without an automated test for the new gate — only an inline regex verification at PR time.
  3. Can't reach the next gate. The deferred binary-vs-file capability freshness gate (workflow#758 cycle 4-A1 I3) requires spawning the just-released plugin binary and calling its GetContractRegistry RPC, then diffing against the committed plugin.json.capabilities. Bash + jq cannot do that; wfctl already has plugin-spawn infrastructure (plugin/external/manager.go).
  4. Two source-of-truth problem. wfctl is the plugin-contract authority (it owns PluginManifest.Validate, ParseSemver, the gRPC client). Registry sync logic that knows about plugin manifests, version semantics, and capability shapes belongs in the same domain.
  5. Long-tail of bespoke shell logicfetch_plugin_json shells gh api, downloads_match_version is a custom jq filter, release_downloads parses JSON via bash. Each has been a small regression hazard.

Proposed direction

New wfctl registry sync subcommand. Walks plugins/<name>/manifest.json in a checked-out workflow-registry repo, performs the same tag-fetch / version-compare / capability-fetch / manifest-rewrite logic as sync-versions.sh today, plus:

  • Strict-semver tag gate (single regex; same source as wfctl plugin validate-contract --for-publish).
  • (Future, deferred I3) --verify-capabilities flag: pull the just-released plugin tarball, exec the binary, call GetContractRegistry, diff against committed plugin.json.capabilities. Reject if stale.
  • (Future) --apply writes back; absent it's dry-run.

workflow-registry's GitHub Action becomes a one-line wfctl registry sync --apply (or whatever flag set).

Bundles with

This issue also bundles the related deferred work that all benefits from a Go-side migration:

Deferred from workflow#758

  • Layer 3b: apply plugin-version discipline to remaining 56 plugin repos (workflow#760). Mechanical fan-out; the canonical pattern shipped 2026-05-23. Audit list enumerated in Layer 3b: apply plugin-version discipline to remaining 56 plugin repos (workflow#758 follow-up) #760.
  • Binary-vs-file capability freshness gate at contract-check time (cycle 4-A1 I3). Natural fit for wfctl registry sync --verify-capabilities AND wfctl plugin validate-contract --for-publish --verify-binary (operator-side).
  • Full SemVer 2.0.0 prerelease support: requires concerted ParseSemver + sync-versions + wfctl install update. Naturally part of this scope if registry sync moves to Go.
  • Gap-repos: ~8 plugin repos lack release pipelines (agent, cms, compute, cloud-ui, data-protection, edge-compute, sandbox, waf). Separate from this migration but should be audited alongside.

Audit needed

Before brainstorming the design, an audit pass to inventory:

  1. Every shell-script in workflow-registry/scripts/ (not just sync-versions.sh) — what else lives there, what calls it?
  2. Existing wfctl plugin subcommand surface — what's the precedent for wfctl registry <verb> vs wfctl plugin registry-<verb>?
  3. workflow-registry's CI invocations of these scripts — what's the actual contract Github Actions consume?
  4. Any in-tree consumers besides CI (cron, manual runs by operators, downstream tooling)?
  5. The 56 Layer 3b repos: per-repo variance preview so the fan-out doesn't hit surprises mid-sweep (DO/AWS/GCP/Azure/github pilot already revealed: Version var name varies, main.go path varies, IaC vs non-IaC split, capabilities-shape variance).

Acceptance criteria

  • wfctl registry sync subcommand lands with table-driven tests
  • workflow-registry/scripts/sync-versions.sh deleted (or reduced to a single-line wfctl invocation)
  • Same strict-semver tag gate sourced from one regex constant in Go
  • --verify-capabilities (or equivalent) closes cycle 4-A1 I3
  • Layer 3b sweep (56 repos) completes
  • Gap-repos audited; per-repo path forward documented
  • Full SemVer 2.0.0 evaluated: ship the parser update OR explicitly defer with rationale

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions