How to validate a registry in CI
Make a registry's CI job fail when a resource it ships is malformed, missing from discovery, or cannot be compiled for the harnesses your consumers use.
Prerequisites
- A CI runner with Git, plus either network access to GitHub releases or Go to build from source.
- A checkout of the registry repository.
- The registry's source layout (
claude,factory,cursor,windsurf,gemini,devin, orcodex).
1. Install a pinned CLI
Pin the release your consumers use, so CI results match what they run. Don't track the newest release. A new release can change diagnostics and make an unchanged registry fail. The examples pin v4.1.3. Run these blocks in one Bash script or one GitHub Actions run step, and choose one installation option. Later blocks reuse BIN_DIR, PATH, CONSUMER, and the working directory. Prepare a job-owned bin directory first, so either installation path below works:
set -euo pipefail
BIN_DIR="$(mktemp -d)"
export PATH="$BIN_DIR:$PATH"The prep commands print nothing. Next, download the release asset for the pinned version:
# Download a pinned release binary (Linux runner example)
VERSION="v4.1.3"
ARCH="linux-amd64" # or linux-arm64, darwin-arm64, darwin-amd64
curl -fsSL "https://github.com/Adyen/agent-marketplace/releases/download/${VERSION}/agent-marketplace-${VERSION}-${ARCH}" -o "$BIN_DIR/agent-marketplace"
chmod +x "$BIN_DIR/agent-marketplace"
agent-marketplace --versionOr build from source at the pinned tag:
SOURCE_DIR="$(mktemp -d)"
git clone --depth 1 --branch v4.1.3 https://github.com/Adyen/agent-marketplace.git "$SOURCE_DIR"
(cd "$SOURCE_DIR" && CGO_ENABLED=0 go build -ldflags "-X main.version=v4.1.3" -o "$BIN_DIR/agent-marketplace" ./cmd/agent-marketplace)
rm -rf -- "$SOURCE_DIR"
agent-marketplace --versioncurl, chmod, go build, and the rm -rf cleanup print nothing. git clone prints progress lines. Either way, the last command prints v4.1.3. The -ldflags value makes --version print the tag. Remove the mktemp directories at the end of the job unless your runner cleans its workspace; they hold only files this job created. There is no Windows build.
2. Point a throwaway consumer at the checkout
REGISTRY_PATH=$PWD
CONSUMER=$(mktemp -d)
mkdir -p "$CONSUMER/.agents"
cat > "$CONSUMER/.agents/config.yaml" << EOF
apiVersion: agent-marketplace/v4
harnesses:
- claude
- factory
- cursor
- windsurf
- gemini
- devin
- codex
sources:
checkout:
directory: "${REGISTRY_PATH}"
resources:
checkout:
all: true
required: true
EOFThe commands print nothing and write the throwaway config to "$CONSUMER/.agents/config.yaml".
- List every harness your consumers target. A resource that compiles for Claude Code can still produce an error for Codex or a warning for Windsurf.
- A
directorysource reads the checkout directly and creates no lockfile, which is what you want in CI. all: trueselects every resource of every kind.required: truestops an exclusion in another configuration layer on the runner from hiding a resource.
3. Sync strictly and check the summary
cd "$CONSUMER"
agent-marketplace sync --strict -v | tee sync.log
grep -q 'Malformed 0 Errors' sync.log || { echo "registry has malformed resources" >&2; exit 1; }
agent-marketplace sync --checkA clean sync ends with a summary such as Installed 12 Removed 0 Malformed 0 Errors 0 (counts vary with the registry). sync --check exits with status 0 when there is no drift; warnings may still print.
syncexits with status1when any resource fails to compile for a selected harness (Errorsgreater than0). Warnings, such as a subagent that Windsurf cannot represent, do not fail the run.--strictmakes sync exit with status1when a resource named in a configuration or a collection is missing or fails to load.all: truealso selects every collection, so this checks that every collection member exists.- With
all: true, a resource that fails to load is left out of the catalog and counted underMalformedwithout failing the run, even with--strict. Thegrepline catches it.-vlists each malformed resource with its path and error. sync --checkthen exits with status1if the output on disk does not match what sync would write, which would mean the result is not stable.pipefail, set at the start of the job, keeps a failingsyncfrom being hidden bytee, which exits with status0on its own. Run the job with bash (shell: bashin GitHub Actions); dash, which is/bin/shon Debian and Ubuntu, rejectsset -o pipefail.
4. Confirm what was discovered
Check that every resource you expect is found, in addition to checking that nothing failed:
agent-marketplace list --kind skill
agent-marketplace list --kind command
agent-marketplace list --kind subagent
agent-marketplace list --kind hook
agent-marketplace list --kind mcp
agent-marketplace list --kind collectionEach command prints a REGISTRY KIND NAME TAGS (metadata) STATUS table. REGISTRY shows the registry directory's base name, not the source key; typical output:
REGISTRY KIND NAME TAGS (metadata) STATUS
<registry-directory> skill code-review review,quality availableCheck a row for every resource you expect: compare the printed NAME and KIND values against a checked-in list of expected resources, and make that comparison exit nonzero when an expected resource is missing. list exits 0 whether or not the table contains the row, so without the check a resource placed in a directory the layout does not read would silently ship missing.
Troubleshooting
- Diagnostics in CI differ from local runs: compare the CLI version (
agent-marketplace --version) and theharnesseslist. - Sync writes for
factoryandwindsurfonly: the throwaway config has noharnesses, so sync falls back to the user configuration. On a fresh runner, the first command creates~/.agents/config.yamlwith those two harnesses. - A resource is missing from
list: it is in a directory the source layout does not read. See the registry layout reference. - A resource is malformed with an
ingest.*code: look the code up in the diagnostics reference or inpkg/harness-compilation/diag/codes.md.