Feat v0.8.2 capability negotiation (#69)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Has been skipped
CI/CD / test-runners (push) Has been skipped
CI/CD / e2e-smoke (push) Has been skipped
CI/CD / test-go-pg (push) Successful in 2m47s
CI/CD / test-sqlite (push) Successful in 2m52s
CI/CD / build-and-deploy (push) Successful in 30s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #69.
This commit is contained in:
2026-04-03 09:14:00 +00:00
committed by xcaliber
parent 435f972ded
commit 00ef970163
16 changed files with 593 additions and 96 deletions

View File

@@ -0,0 +1,146 @@
package handlers
import (
"context"
"database/sql"
"log"
"github.com/gin-gonic/gin"
"armature/storage"
)
// ── Capability detection ────────────────────────────────────────────
// DetectCapabilities probes the runtime environment and returns a map
// of capability name → available. Called once at startup and again on
// each admin GET /capabilities request.
func DetectCapabilities(db *sql.DB, isPostgres bool, objStore storage.ObjectStore, workspaceRoot string) map[string]bool {
caps := map[string]bool{
"postgres": false,
"pgvector": false,
"object_storage": false,
"s3": false,
"workspace": false,
}
// postgres: dialect check
if isPostgres {
caps["postgres"] = true
}
// pgvector: query pg_extension (PG only)
if isPostgres && db != nil {
var one int
err := db.QueryRow("SELECT 1 FROM pg_extension WHERE extname = 'vector'").Scan(&one)
caps["pgvector"] = err == nil
}
// object_storage: configured and healthy
if objStore != nil {
if err := objStore.Healthy(context.Background()); err == nil {
caps["object_storage"] = true
}
}
// s3: specific backend check
if objStore != nil && objStore.Backend() == "s3" {
caps["s3"] = true
}
// workspace: root exists and is writable
if workspaceRoot != "" && storage.IsPathWritable(workspaceRoot) {
caps["workspace"] = true
}
return caps
}
// ── Manifest parsing ────────────────────────────────────────────────
// Capabilities holds the parsed capabilities block from a package manifest.
type Capabilities struct {
Required []string
Optional []string
}
// ParseCapabilities extracts capabilities.required and capabilities.optional
// from a manifest map. Returns zero value + false if no capabilities block.
func ParseCapabilities(manifest map[string]any) (Capabilities, bool) {
capsRaw, ok := manifest["capabilities"].(map[string]any)
if !ok {
return Capabilities{}, false
}
var caps Capabilities
if req, ok := capsRaw["required"].([]any); ok {
for _, v := range req {
if s, ok := v.(string); ok && s != "" {
caps.Required = append(caps.Required, s)
}
}
}
if opt, ok := capsRaw["optional"].([]any); ok {
for _, v := range opt {
if s, ok := v.(string); ok && s != "" {
caps.Optional = append(caps.Optional, s)
}
}
}
return caps, true
}
// CheckRequiredCapabilities returns the subset of required capabilities
// that are not present in the detected set.
func CheckRequiredCapabilities(required []string, detected map[string]bool) []string {
var missing []string
for _, r := range required {
if !detected[r] {
missing = append(missing, r)
}
}
return missing
}
// capabilityHelpText returns operator-facing remediation hints keyed by
// capability name.
func capabilityHelpText(missing []string) map[string]string {
hints := map[string]string{
"postgres": "Switch DB_DRIVER to postgres and configure DATABASE_URL.",
"pgvector": "Run `CREATE EXTENSION vector;` in your PostgreSQL database.",
"object_storage": "Configure STORAGE_BACKEND (pvc or s3) and ensure the backend is healthy.",
"s3": "Set STORAGE_BACKEND=s3 and configure S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY.",
"workspace": "Set WORKSPACE_ROOT to a writable directory path.",
}
result := make(map[string]string, len(missing))
for _, m := range missing {
if h, ok := hints[m]; ok {
result[m] = h
} else {
result[m] = "Unknown capability — check package documentation."
}
}
return result
}
// ── Admin endpoint ──────────────────────────────────────────────────
// CapabilitiesHandler serves the admin capabilities endpoint.
type CapabilitiesHandler struct {
detect func() map[string]bool
}
// NewCapabilitiesHandler creates a handler that re-probes capabilities on
// each request so the response reflects current state.
func NewCapabilitiesHandler(detect func() map[string]bool) *CapabilitiesHandler {
return &CapabilitiesHandler{detect: detect}
}
// GetCapabilities returns detected environment capabilities.
// GET /api/v1/admin/capabilities
func (h *CapabilitiesHandler) GetCapabilities(c *gin.Context) {
caps := h.detect()
log.Printf("[capabilities] probed: %v", caps)
c.JSON(200, caps)
}

View File

@@ -0,0 +1,205 @@
package handlers
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
// ── ParseCapabilities ───────────────────────────────────────────────
func TestParseCapabilities_Full(t *testing.T) {
manifest := map[string]any{
"id": "test-pkg",
"capabilities": map[string]any{
"required": []any{"postgres", "pgvector"},
"optional": []any{"s3"},
},
}
caps, ok := ParseCapabilities(manifest)
if !ok {
t.Fatal("expected ok=true")
}
if len(caps.Required) != 2 || caps.Required[0] != "postgres" || caps.Required[1] != "pgvector" {
t.Errorf("required = %v, want [postgres pgvector]", caps.Required)
}
if len(caps.Optional) != 1 || caps.Optional[0] != "s3" {
t.Errorf("optional = %v, want [s3]", caps.Optional)
}
}
func TestParseCapabilities_Missing(t *testing.T) {
manifest := map[string]any{"id": "test-pkg"}
_, ok := ParseCapabilities(manifest)
if ok {
t.Error("expected ok=false when no capabilities block")
}
}
func TestParseCapabilities_EmptyArrays(t *testing.T) {
manifest := map[string]any{
"capabilities": map[string]any{
"required": []any{},
"optional": []any{},
},
}
caps, ok := ParseCapabilities(manifest)
if !ok {
t.Fatal("expected ok=true")
}
if len(caps.Required) != 0 {
t.Errorf("required should be empty, got %v", caps.Required)
}
if len(caps.Optional) != 0 {
t.Errorf("optional should be empty, got %v", caps.Optional)
}
}
func TestParseCapabilities_IgnoresEmpty(t *testing.T) {
manifest := map[string]any{
"capabilities": map[string]any{
"required": []any{"pgvector", "", "postgres"},
},
}
caps, _ := ParseCapabilities(manifest)
if len(caps.Required) != 2 {
t.Errorf("expected 2 entries (empty filtered), got %v", caps.Required)
}
}
// ── CheckRequiredCapabilities ───────────────────────────────────────
func TestCheckRequired_AllMet(t *testing.T) {
detected := map[string]bool{"postgres": true, "pgvector": true, "s3": true}
missing := CheckRequiredCapabilities([]string{"postgres", "pgvector"}, detected)
if len(missing) != 0 {
t.Errorf("expected no missing, got %v", missing)
}
}
func TestCheckRequired_SomeUnmet(t *testing.T) {
detected := map[string]bool{"postgres": true, "pgvector": false, "s3": false}
missing := CheckRequiredCapabilities([]string{"postgres", "pgvector", "s3"}, detected)
if len(missing) != 2 {
t.Errorf("expected 2 missing, got %v", missing)
}
}
func TestCheckRequired_EmptyRequired(t *testing.T) {
detected := map[string]bool{"postgres": true}
missing := CheckRequiredCapabilities(nil, detected)
if len(missing) != 0 {
t.Errorf("expected no missing for nil required, got %v", missing)
}
}
func TestCheckRequired_UnknownCap(t *testing.T) {
detected := map[string]bool{"postgres": true}
missing := CheckRequiredCapabilities([]string{"gpu"}, detected)
if len(missing) != 1 || missing[0] != "gpu" {
t.Errorf("expected [gpu], got %v", missing)
}
}
// ── capabilityHelpText ──────────────────────────────────────────────
func TestCapabilityHelpText_KnownCaps(t *testing.T) {
hints := capabilityHelpText([]string{"pgvector", "s3"})
if len(hints) != 2 {
t.Fatalf("expected 2 hints, got %d", len(hints))
}
if hints["pgvector"] == "" {
t.Error("pgvector hint is empty")
}
if hints["s3"] == "" {
t.Error("s3 hint is empty")
}
}
func TestCapabilityHelpText_UnknownCap(t *testing.T) {
hints := capabilityHelpText([]string{"gpu"})
if hints["gpu"] == "" {
t.Error("expected fallback hint for unknown cap")
}
}
// ── DetectCapabilities (SQLite path — no real DB) ───────────────────
func TestDetectCapabilities_SQLite_NilStore(t *testing.T) {
caps := DetectCapabilities(nil, false, nil, "")
if caps["postgres"] {
t.Error("postgres should be false on SQLite")
}
if caps["pgvector"] {
t.Error("pgvector should be false on SQLite")
}
if caps["object_storage"] {
t.Error("object_storage should be false with nil store")
}
if caps["s3"] {
t.Error("s3 should be false with nil store")
}
if caps["workspace"] {
t.Error("workspace should be false with empty root")
}
}
func TestDetectCapabilities_WorkspaceWritable(t *testing.T) {
tmpDir := t.TempDir()
caps := DetectCapabilities(nil, false, nil, tmpDir)
if !caps["workspace"] {
t.Error("workspace should be true for writable temp dir")
}
}
func TestDetectCapabilities_WorkspaceUnwritable(t *testing.T) {
caps := DetectCapabilities(nil, false, nil, "/nonexistent/path/xyz")
if caps["workspace"] {
t.Error("workspace should be false for nonexistent path")
}
}
// ── Admin endpoint ──────────────────────────────────────────────────
func TestGetCapabilities_HTTP(t *testing.T) {
gin.SetMode(gin.TestMode)
h := NewCapabilitiesHandler(func() map[string]bool {
return map[string]bool{
"postgres": false,
"pgvector": false,
"object_storage": true,
"s3": false,
"workspace": true,
}
})
r := gin.New()
r.GET("/api/v1/admin/capabilities", h.GetCapabilities)
req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/capabilities", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != 200 {
t.Fatalf("status = %d, want 200", w.Code)
}
var result map[string]bool
if err := json.Unmarshal(w.Body.Bytes(), &result); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if result["postgres"] {
t.Error("postgres should be false")
}
if !result["object_storage"] {
t.Error("object_storage should be true")
}
if !result["workspace"] {
t.Error("workspace should be true")
}
}

View File

@@ -38,6 +38,7 @@ type PackageHandler struct {
sandbox *sandbox.Sandbox
runner *sandbox.Runner
hub *events.Hub
capabilities map[string]bool // detected environment capabilities
}
func NewPackageHandler(s store.Stores, packagesDir ...string) *PackageHandler {
@@ -69,6 +70,12 @@ func (h *PackageHandler) SetHub(hub *events.Hub) {
h.hub = hub
}
// SetCapabilities stores the detected environment capabilities map.
// Used at install time to validate manifest capabilities.required.
func (h *PackageHandler) SetCapabilities(caps map[string]bool) {
h.capabilities = caps
}
// broadcastPackageChanged emits a package.changed event to all clients.
func (h *PackageHandler) broadcastPackageChanged(action, id string) {
if h.hub == nil {
@@ -286,8 +293,23 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
}
}
// Phase 10: Check capabilities → mark dormant if unmet
isDormant := h.checkCapabilities(c, pkgID, manifest, &preservedEnabled)
// Phase 10: Validate required capabilities
if caps, ok := ParseCapabilities(manifest); ok {
missing := CheckRequiredCapabilities(caps.Required, h.capabilities)
if len(missing) > 0 {
h.stores.Packages.Delete(c.Request.Context(), pkgID)
c.JSON(http.StatusUnprocessableEntity, gin.H{
"error": "missing required capabilities",
"missing": missing,
"help": capabilityHelpText(missing),
})
return
}
unmetOpt := CheckRequiredCapabilities(caps.Optional, h.capabilities)
if len(unmetOpt) > 0 {
log.Printf("[packages] %s: optional capabilities unavailable: %v", pkgID, unmetOpt)
}
}
resp := gin.H{
"id": pkgID,
@@ -297,9 +319,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
"source": pkgSource,
"enabled": preservedEnabled,
}
if isDormant {
resp["status"] = "dormant"
}
c.JSON(http.StatusOK, resp)
h.broadcastPackageChanged("installed", pkgID)
}
@@ -591,7 +610,7 @@ func (h *PackageHandler) resolveDependencies(c *gin.Context, pkgID string, manif
pkgPath := filepath.Join(h.bundledDir, libID+".pkg")
if _, statErr := os.Stat(pkgPath); statErr == nil {
log.Printf("[packages] auto-installing bundled dependency %s for %s", libID, pkgID)
if _, installErr := installBundledPackage(pkgPath, h.packagesDir, h.stores, h.runner, c.GetString("user_id")); installErr != nil {
if _, installErr := installBundledPackage(pkgPath, h.packagesDir, h.stores, h.runner, c.GetString("user_id"), h.capabilities); installErr != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": fmt.Sprintf("dependency %s auto-install failed: %v", libID, installErr),
})
@@ -634,27 +653,6 @@ func (h *PackageHandler) resolveDependencies(c *gin.Context, pkgID string, manif
return nil
}
// checkCapabilities marks a package dormant if it declares unmet `requires` entries.
func (h *PackageHandler) checkCapabilities(c *gin.Context, pkgID string, manifest map[string]any, preservedEnabled *bool) bool {
knownCaps := map[string]bool{}
var unmetReqs []string
if reqs, ok := manifest["requires"].([]any); ok && len(reqs) > 0 {
for _, r := range reqs {
req, _ := r.(string)
if req != "" && !knownCaps[req] {
unmetReqs = append(unmetReqs, req)
}
}
}
if len(unmetReqs) == 0 {
return false
}
h.stores.Packages.SetStatus(c.Request.Context(), pkgID, "dormant")
h.stores.Packages.SetEnabled(c.Request.Context(), pkgID, false)
*preservedEnabled = false
log.Printf("[packages] %s: dormant (unmet requires: %v)", pkgID, unmetReqs)
return true
}
// extractableRelPath returns the relative path for a zip entry if it
// belongs to an extractable directory (js/, css/, assets/, star/) or

View File

@@ -40,7 +40,7 @@ var defaultBundledPackages = map[string]bool{}
//
// Design: install-once, skip-if-present. If an admin uninstalls a bundled
// package, it won't be re-installed on the next restart.
func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores store.Stores, runner *sandbox.Runner) {
func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores store.Stores, runner *sandbox.Runner, detectedCaps map[string]bool) {
entries, err := os.ReadDir(bundledDir)
if err != nil {
if os.IsNotExist(err) {
@@ -82,7 +82,7 @@ func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores st
}
pkgPath := filepath.Join(bundledDir, entry.Name())
result, err := installBundledPackage(pkgPath, packagesDir, stores, runner, adminUserID)
result, err := installBundledPackage(pkgPath, packagesDir, stores, runner, adminUserID, detectedCaps)
if err != nil {
log.Printf("[bundled] ⚠️ Failed to install %s: %v", entry.Name(), err)
continue
@@ -129,7 +129,7 @@ func parseBundleAllowlist(s string) map[string]bool {
// installBundledPackage installs a single .pkg archive if its package ID
// doesn't already exist in the database. Returns "installed" or "skipped".
func installBundledPackage(pkgPath, packagesDir string, stores store.Stores, runner *sandbox.Runner, adminUserID string) (string, error) {
func installBundledPackage(pkgPath, packagesDir string, stores store.Stores, runner *sandbox.Runner, adminUserID string, detectedCaps map[string]bool) (string, error) {
ctx := context.Background()
// Open as zip
@@ -237,20 +237,17 @@ func installBundledPackage(pkgPath, packagesDir string, stores store.Stores, run
}
}
// Handle dormant status for packages with unmet requires
knownCaps := map[string]bool{}
if reqs, ok := manifest["requires"].([]any); ok && len(reqs) > 0 {
var unmetReqs []string
for _, r := range reqs {
req, _ := r.(string)
if req != "" && !knownCaps[req] {
unmetReqs = append(unmetReqs, req)
}
// Validate required capabilities — skip package if unmet
if caps, ok := ParseCapabilities(manifest); ok {
missing := CheckRequiredCapabilities(caps.Required, detectedCaps)
if len(missing) > 0 {
stores.Packages.Delete(ctx, pkgID)
log.Printf("[bundled] %s: skipped (missing required capabilities: %v)", pkgID, missing)
return "skipped", nil
}
if len(unmetReqs) > 0 {
stores.Packages.SetStatus(ctx, pkgID, "dormant")
stores.Packages.SetEnabled(ctx, pkgID, false)
log.Printf("[bundled] %s: dormant (unmet requires: %v)", pkgID, unmetReqs)
unmetOpt := CheckRequiredCapabilities(caps.Optional, detectedCaps)
if len(unmetOpt) > 0 {
log.Printf("[bundled] %s: optional capabilities unavailable: %v", pkgID, unmetOpt)
}
}

View File

@@ -88,7 +88,7 @@ func TestBundledInstall_FreshDB(t *testing.T) {
"route": "/s/test-b",
})
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
// Both packages should be installed and enabled
for _, id := range []string{"test-surface-a", "test-surface-b"} {
@@ -127,7 +127,7 @@ func TestBundledInstall_SkipsExisting(t *testing.T) {
"type": "surface",
})
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
// Package should retain original title (not overwritten)
pkg, err := stores.Packages.Get(ctx, "pre-existing")
@@ -145,7 +145,7 @@ func TestBundledInstall_MissingDir(t *testing.T) {
stores := newTestStores(t)
// Should not panic or error — just log and return
InstallBundledPackages("/nonexistent/path", "", "", stores, nil)
InstallBundledPackages("/nonexistent/path", "", "", stores, nil, nil)
}
// TestBundledInstall_EmptyDir verifies no-op when directory exists but is empty.
@@ -155,12 +155,12 @@ func TestBundledInstall_EmptyDir(t *testing.T) {
bundledDir := t.TempDir()
// Should not panic or error
InstallBundledPackages(bundledDir, "", "", stores, nil)
InstallBundledPackages(bundledDir, "", "", stores, nil, nil)
}
// TestBundledInstall_DormantPackage verifies that bundled packages with
// unmet requires are marked dormant.
func TestBundledInstall_DormantPackage(t *testing.T) {
// TestBundledInstall_SkippedOnUnmetRequiredCaps verifies that bundled
// packages with unmet capabilities.required are skipped (not registered).
func TestBundledInstall_SkippedOnUnmetRequiredCaps(t *testing.T) {
stores := newTestStores(t)
ctx := context.Background()
@@ -168,24 +168,21 @@ func TestBundledInstall_DormantPackage(t *testing.T) {
packagesDir := t.TempDir()
buildTestPkg(t, bundledDir, map[string]any{
"id": "dormant-pkg",
"title": "Dormant Package",
"type": "extension",
"hooks": []string{"on_install"},
"requires": []string{"chat"},
"id": "needs-pgvector",
"title": "Needs pgvector",
"type": "extension",
"hooks": []string{"on_install"},
"capabilities": map[string]any{
"required": []string{"pgvector"},
},
})
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
// No capabilities detected → pgvector is unavailable
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
pkg, err := stores.Packages.Get(ctx, "dormant-pkg")
if err != nil || pkg == nil {
t.Fatal("dormant package not found after install")
}
if pkg.Status != "dormant" {
t.Errorf("status = %q, want %q", pkg.Status, "dormant")
}
if pkg.Enabled {
t.Error("dormant package should not be enabled")
pkg, _ := stores.Packages.Get(ctx, "needs-pgvector")
if pkg != nil {
t.Error("package with unmet required capabilities should not be registered")
}
}
@@ -209,7 +206,7 @@ func TestBundledInstall_Allowlist(t *testing.T) {
})
// Only allow alpha and gamma
InstallBundledPackages(bundledDir, packagesDir, "pkg-alpha,pkg-gamma", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "pkg-alpha,pkg-gamma", stores, nil, nil)
// Alpha and gamma should be installed
for _, id := range []string{"pkg-alpha", "pkg-gamma"} {

View File

@@ -459,7 +459,7 @@ func TestBundledInstall_DefaultAllowlist(t *testing.T) {
})
// Empty allowlist → empty default set → nothing installed
InstallBundledPackages(bundledDir, packagesDir, "", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "", stores, nil, nil)
pkg, _ := stores.Packages.Get(ctx, "notes")
if pkg != nil {
@@ -484,7 +484,7 @@ func TestBundledInstall_WildcardAllowlist(t *testing.T) {
"id": "any-pkg", "title": "Any", "type": "surface", "version": "1.0.0",
})
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
pkg, err := stores.Packages.Get(ctx, "any-pkg")
if err != nil || pkg == nil {

View File

@@ -411,7 +411,7 @@ func TestUpgrade_BundledSkipExistingOnRestart(t *testing.T) {
})
// First install
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
pkg, _ := stores.Packages.Get(ctx, "restart-pkg")
if pkg == nil {
@@ -427,7 +427,7 @@ func TestUpgrade_BundledSkipExistingOnRestart(t *testing.T) {
})
// Second install — should skip because package exists
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
pkg, _ = stores.Packages.Get(ctx, "restart-pkg")
if pkg.Version != "1.0.0" {
@@ -438,7 +438,7 @@ func TestUpgrade_BundledSkipExistingOnRestart(t *testing.T) {
}
}
func TestUpgrade_PackageDormantOnUnmetRequires(t *testing.T) {
func TestUpgrade_PackageSkippedOnUnmetRequiredCaps(t *testing.T) {
stores := newTestStores(t)
ctx := context.Background()
@@ -447,25 +447,22 @@ func TestUpgrade_PackageDormantOnUnmetRequires(t *testing.T) {
// Package requires a capability that doesn't exist
buildTestPkg(t, bundledDir, map[string]any{
"id": "future-pkg",
"title": "Future",
"type": "extension",
"version": "1.0.0",
"hooks": []string{"on_install"},
"requires": []string{"kernel>=99.0.0"},
"id": "future-pkg",
"title": "Future",
"type": "extension",
"version": "1.0.0",
"hooks": []string{"on_install"},
"capabilities": map[string]any{
"required": []string{"pgvector"},
},
})
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil)
// No capabilities detected → pgvector is unavailable
InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil, nil)
pkg, _ := stores.Packages.Get(ctx, "future-pkg")
if pkg == nil {
t.Fatal("package should still be registered")
}
if pkg.Status != "dormant" {
t.Errorf("status = %q, want %q", pkg.Status, "dormant")
}
if pkg.Enabled {
t.Error("dormant package should not be enabled")
if pkg != nil {
t.Error("package with unmet required capabilities should not be registered")
}
}