Skip to content

fix: verify marketplace skill installs#244

Merged
pedronauck merged 2 commits into
compozy:mainfrom
Viniciuscarvalho:codex/fix-skills-marketplace-verified-install
Jun 1, 2026
Merged

fix: verify marketplace skill installs#244
pedronauck merged 2 commits into
compozy:mainfrom
Viniciuscarvalho:codex/fix-skills-marketplace-verified-install

Conversation

@Viniciuscarvalho

@Viniciuscarvalho Viniciuscarvalho commented May 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Marketplace skill installs now fail unless the installed skill is visible and usable through normal skill discovery after the install.
  • The Web/API path returns a 503 with an actionable message instead of reporting false success when the refreshed registry cannot resolve the installed skill.
  • The CLI install path reloads discovery and rejects disabled, shadowed, missing-provenance, or mismatched marketplace installs.

Changes

  • API: Verify installed marketplace skills after RefreshGlobal() and map failed visibility checks to 503 Service Unavailable.
  • CLI: Reload the skill registry after agh skill install and validate marketplace source, provenance slug, and enabled state before printing success.
  • Tests: Add regression coverage for API false-success prevention and CLI disabled-discovery rejection.

Release Notes

Bug Fixes

  • Marketplace skill installs no longer report success until the installed skill is visible and usable through skill discovery.

Test plan

  • PATH=/opt/homebrew/bin:$PATH go test ./internal/api/core ./internal/cli ./internal/skills/marketplace -count=1
  • PATH=/opt/homebrew/bin:$PATH TURBO_CONCURRENCY=1 VITEST_MAX_WORKERS=1 GOMAXPROCS=2 make verify completed on the pre-sync base in /tmp/aghv2.vggp8p.
  • PATH=/opt/homebrew/bin:$PATH TURBO_CONCURRENCY=1 VITEST_MAX_WORKERS=1 GOMAXPROCS=2 MAGE=/tmp/agh-mage make verify was retried after rebasing onto a71b6a7f, passed installer check, bun-lint, and bun-typecheck, then the local process was killed during @agh/ui:test (Error: signal: killed). This appears to be local resource pressure; CI should run the full gate.

Refs Viniciuscarvalho#2.

Summary by CodeRabbit

  • New Features

    • Installer and CLI now verify that marketplace skills are discoverable after installation and abort with an error if verification fails.
  • Bug Fixes

    • Unresolvable marketplace installs now return Service Unavailable (503) and emit a warning when discovery/visibility checks fail.
  • Tests

    • Added and extended tests covering visibility verification and various unavailable/install-failure scenarios.

@vercel

vercel Bot commented May 31, 2026

Copy link
Copy Markdown

@Viniciuscarvalho is attempting to deploy a commit to the Compozy Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds post-install visibility verification for marketplace skills: a new ErrUnavailable sentinel, SkillResolver and VerifyInstallVisible checks, API and CLI calls to verify installed skills are discoverable with matching provenance/slug and enabled, plus tests exercising failure modes.

Changes

Marketplace Install Visibility Verification

Layer / File(s) Summary
Service: error, resolver, and verification
internal/skills/marketplace/service.go
Adds ErrUnavailable, SkillResolver interface, VerifyInstallVisible, and installVisibilityLocation helper to validate post-install discoverability, provenance, slug, and enabled state.
Service: verification unit tests
internal/skills/marketplace/service_test.go
Adds fakeSkillResolver and TestVerifyInstallVisible table-driven tests covering missing discovery, shadowing, missing provenance, slug mismatch, disabled skills, and nil resolver.
API: HTTP mapping, handler verification and tests
internal/api/core/errors.go, internal/api/core/skills.go, internal/api/core/skills_test.go
Maps ErrUnavailable to HTTP 503; InstallSkillMarketplace calls skillmarketplace.VerifyInstallVisible after registry refresh and logs verification failures; tests add import/cases for 503 and a subtest asserting 503 when the installed skill isn't visible after refresh.
CLI: post-install verification and tests
internal/cli/skill_marketplace.go, internal/cli/skill_test.go
CLI installMarketplaceSkill calls verifyInstalledMarketplaceSkill which reloads the bundled/user registry and delegates to VerifyInstallVisible; test ensures install is rejected when discovery is disabled for the skill.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding verification for marketplace skill installs to ensure they are visible and discoverable after installation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pedronauck pedronauck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-LLM peer review — PR #244 "fix: verify marketplace skill installs"

Two independent implementation peer reviews were run against the PR head (e9ca643f, base main), each reading the full post-change files in an isolated worktree, not just the hunks:

  • Reviewer A — Claude Opus (--ide claude --model opus --reasoning-effort xhigh) → verdict SHIP (0 blockers, 7 risks, 3 nits)
  • Reviewer B — Codex (--ide codex --model gpt-5.5 --reasoning-effort xhigh) → verdict FIX_BEFORE_SHIP (2 blockers, 2 risks, 0 nits)

Aggregate verdict: 🟡 FIX_BEFORE_SHIP (one must-fix; everything else is recommended follow-up).

The change is directionally correct, nil-safe, and well-tested — no correctness, security, concurrency, or persistence defects were found by either reviewer. Codex independently ran go test ./internal/api/core ./internal/cli ./internal/skills/marketplace1757 passed. The reviewers disagree only on severity of one issue (below), and one Codex blocker did not survive verification.


✅ Cross-validated finding (both reviewers caught it independently) — recommend fixing before merge

CLI install-verification failures don't return the new ErrUnavailable sentinel; HTTP/CLI error contracts diverge.
internal/cli/skill_marketplace.go (~L136–174)

The HTTP path wraps every visibility failure with %w … skillmarketplace.ErrUnavailable, so StatusForSkillMarketplaceError classifies it to 503. The CLI helper verifyInstalledMarketplaceSkill returns plain fmt.Errorf(...) for the same five failure modes (not-visible, shadowed source, missing provenance, slug mismatch, disabled), so the identical logical failure is programmatically classifiable on one surface and opaque text on the other. This breaks the HTTP/CLI parity the PR itself establishes and undercuts the agent-manageable / deterministic-error-identity posture (SD-011) for an agent-facing surface.

  • Codex rated this a blocker; Claude rated it a risk (correctly noting it is not a %w violation — these are origin errors — so the question is classification, not wrapping discipline).
  • Reconciliation: it is the PR's own stated parity contract, on a CLI surface agents drive, so it should be closed before merge.
  • Fix: wrap skillmarketplace.ErrUnavailable in every CLI visibility branch (including the findSkillByName miss), then update the CLI regression test to assert errors.Is(err, skillmarketplace.ErrUnavailable) instead of strings.Contains(err.Error(), "visible but disabled"). This also resolves the valid half of Codex B-002.

📋 Recommended follow-ups (risks — strong consensus that these are non-blocking)

  1. Duplicated verification invariant, no shared owner (Claude R-001, N-003) — the five-step "installed skill is discoverable" check is implemented verbatim twice (verifyMarketplaceInstallVisible in internal/api/core/skills.go:499 and verifyInstalledMarketplaceSkill in internal/cli/skill_marketplace.go:115), with two different source-label helpers. Extract one owner in internal/skills(/marketplace) returning a %w-wrapped ErrUnavailable; route both API and CLI through it. This single extraction also fixes the must-fix above and unifies the divergent helper names. (Greenfield: prefer one owner over parallel copies that will drift.)

  2. 503 conflates five non-transient failures (Claude R-003)ErrUnavailable → 503 (internal/api/core/errors.go:529), but four of the five modes (shadowed, disabled, missing-provenance, slug-mismatch) are terminal until the operator acts. 503 invites clients/agents to retry-with-backoff against a condition retrying can't fix. Consider splitting (409 Conflict for shadow/slug, 422 for disabled/missing-provenance) or pick a terminal code and document it as non-retryable.

  3. Install is non-atomic on failed verification (Claude R-004) — order is Install (writes dir + sidecar) → RefreshGlobal → verify. On failure the files remain on disk and the daemon registry has already been refreshed (a disabled/shadowed skill is partially observable via GET /api/skills) while the response reports failure. Either reword as "installed but not discoverable" or remove the just-installed dir for the unrecoverable subset so disk state matches the reported outcome — and document the choice.

  4. Thin predicate test coverage vs. the new contract (Codex R-001 + Claude R-006) — the API test exercises only "not visible after refresh"; the CLI test only "disabled". The helper also enforces source, provenance, slug-match, and the implicit InstallResult.Name == Skill.Meta.Name resolution key. Add focused table coverage for the remaining predicates (with sentinel-identity assertions), and pin the name-resolution coupling with a test.

  5. No structured event on verification failure (Claude R-005) — an install that lands on disk yet is undiscoverable is exactly the operational anomaly to surface. Add a slog.Warn (handler + CLI) with name, source, slug, path, and reason before returning.

  6. Docs / official AGH skill not co-shipped (Claude R-007)agh skill install / the install endpoint can now fail with a brand-new terminal outcome. Per the AGH Impact Audit (Web/Docs + Official AGH skill rows), co-ship agent-facing guidance (react by enable/remove-shadow, not retry) or record an explicit "no doc impact" rationale in the PR body.

  7. Pre-existing brittle classifier nearby (Codex R-002)classifyRegistryLookup (internal/skills/marketplace/service.go:1047) falls back to strings.Contains(err.Error(), " not found"). Not introduced by this PR (it only added the ErrUnavailable var to that file), but adjacent and worth replacing with a typed/sentinel error from the registry boundary.


🔹 Nits (Claude)

  • N-001 internal/api/core/skills.go:510 — "remove the shadowing skill" wording can point operators at the wrong artifact; include the resolved source/path of the winning declaration in the message.
  • N-002 internal/cli/skill_marketplace.go:124 — the fresh skills.NewRegistry for verification is built without skills.WithLogger, so load-time verify warnings fall to slog.Default() instead of the command logger.
  • N-003 divergent helper names (verifyInstalledMarketplaceSkill vs verifyMarketplaceInstallVisible) — unify when extracting the shared helper (R-001).

⚠️ Reviewer claim that did NOT survive verification (excluded from the aggregate)

Codex B-002 — "new CLI subtest omits required t.Parallel()". Verified against the source: no subtest inside TestSkillMarketplaceHelpers calls t.Parallel() (only the parent at skill_test.go:1171); all ~18 siblings run serially, almost certainly due to shared filesystem/registry state via newSkillTestEnv/loadSkillRegistry. The new subtest follows the established local convention, so this is not a test-shape violation. Only the string-assertion half of B-002 is valid, and it is already folded into the must-fix above.


Evidence & method

  • Both reviewers read all 6 changed files in full plus cross-cutting context (internal/skills/registry.go, installer.go, types.go, provenance.go, internal/api/testutil), repo CLAUDE.md set, standing_directives.md, and docs/_memory/lessons/.
  • Codex executed the unit suites for the three touched packages → 1757 tests passed. Neither reviewer ran full make verify (read-only review).
  • Worktree source was confirmed unmutated before and after both runs; each reviewer wrote only its own findings file.
  • Per-reviewer raw findings: .compozy/pr-244/claude/impl-review-findings-round1.md (SHIP) and .compozy/pr-244/codex/impl-review-findings-round1.md (FIX_BEFORE_SHIP). Both passed the cy-impl-peer-review structural validator.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/cli/skill_marketplace.go (1)

141-150: 💤 Low value

Optional: use slog.WarnContext(ctx, ...) to preserve context-scoped attributes.

The helper logs via slog.Warn without a context. Passing ctx through and using slog.WarnContext keeps trace/request attributes attached, consistent with context propagation through the call stack.

♻️ Proposed change
-	if err := skillmarketplace.VerifyInstallVisible(registry, result); err != nil {
-		logSkillMarketplaceInstallVerificationFailure(result, err)
+	if err := skillmarketplace.VerifyInstallVisible(registry, result); err != nil {
+		logSkillMarketplaceInstallVerificationFailure(ctx, result, err)
 		return fmt.Errorf("cli: %w", err)
 	}
-func logSkillMarketplaceInstallVerificationFailure(result skillmarketplace.InstallResult, err error) {
-	slog.Warn(
+func logSkillMarketplaceInstallVerificationFailure(ctx context.Context, result skillmarketplace.InstallResult, err error) {
+	slog.WarnContext(
+		ctx,
 		"skills marketplace: installed skill is not discoverable",
 		"name", result.Name,
 		"source", result.Registry,
 		"slug", result.Slug,
 		"path", result.Path,
 		"reason", err.Error(),
 	)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/skill_marketplace.go` around lines 141 - 150, The function
logSkillMarketplaceInstallVerificationFailure currently uses slog.Warn without a
context; change its signature to accept a context.Context (e.g., add parameter
ctx) and replace slog.Warn with slog.WarnContext(ctx, ...) so context-scoped
attributes are preserved; update all call sites that invoke
logSkillMarketplaceInstallVerificationFailure to pass the appropriate ctx
through (preserving existing result/err usage) and ensure the log key/values
remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/cli/skill_marketplace.go`:
- Around line 141-150: The function
logSkillMarketplaceInstallVerificationFailure currently uses slog.Warn without a
context; change its signature to accept a context.Context (e.g., add parameter
ctx) and replace slog.Warn with slog.WarnContext(ctx, ...) so context-scoped
attributes are preserved; update all call sites that invoke
logSkillMarketplaceInstallVerificationFailure to pass the appropriate ctx
through (preserving existing result/err usage) and ensure the log key/values
remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2ea89981-7fab-4010-b78f-97f6fd310e9a

📥 Commits

Reviewing files that changed from the base of the PR and between e9ca643 and 4fd229d.

⛔ Files ignored due to path filters (3)
  • packages/site/content/runtime/core/configuration/config-toml.mdx is excluded by !**/*.mdx
  • packages/site/content/runtime/core/skills/marketplace.mdx is excluded by !**/*.mdx
  • skills/agh/references/tools-and-skills.md is excluded by !**/*.md
📒 Files selected for processing (6)
  • internal/api/core/skills.go
  • internal/api/core/skills_test.go
  • internal/cli/skill_marketplace.go
  • internal/cli/skill_test.go
  • internal/skills/marketplace/service.go
  • internal/skills/marketplace/service_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/api/core/skills_test.go
  • internal/cli/skill_test.go

@pedronauck pedronauck merged commit d792047 into compozy:main Jun 1, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants