Skip to content
AI & Agents
Skill

/adopting-lib-commons-huma-wrapper

Adopting the lib-commons/v5 shared Huma (OAS 3.1) OpenAPI wrapper + RFC 9457 problem model (commons/net/http/{openapi,problem}) in a Lerian Go service: wire openapi.New/ServeSpec + problem.Install (central >=500 scrub) on BOTH runtime and spec-gen paths, the per-rail

From plugin
ring
20577 skills42 agents1 command
Install
$ npx -y skills add LerianStudio/ring --skill adopting-lib-commons-huma-wrapper --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/adopting-lib-commons-huma-wrapper

Context preview

The summary Claude sees to decide when to auto-load this skill.

Adopting the lib-commons/v5 shared Huma (OAS 3.1) OpenAPI wrapper + RFC 9457 problem model (commons/net/http/{openapi,problem}) in a Lerian Go service: wire openapi.New/ServeSpec + problem.Install (central >=500 scrub) on BOTH runtime and spec-gen paths, the per-rail

SKILL.md

adopting-lib-commons-huma-wrapper.SKILL.md
name: ring:adopting-lib-commons-huma-wrapper
description: "Adopting the lib-commons/v5 shared Huma (OAS 3.1) OpenAPI wrapper + RFC 9457 problem model (commons/net/http/{openapi,problem}) in a Lerian Go service: wire openapi.New/ServeSpec + problem.Install (central >=500 scrub) on BOTH runtime and spec-gen paths, the per-rail problem.MapError flex seam, and rename-only spec regen. Orchestrates a gated cycle dispatching ring:backend-go. Use for greenfield Huma or migrating a service off a local openapi/humaerr wrapper. Skip for non-Go or non-HTTP work."

Adopt the lib-commons Huma OpenAPI Wrapper

When to use

  • A Go service needs code-first OpenAPI 3.1 over Fiber (Huma) with RFC 9457 `application/problem+json` errors
  • User asks to "adopt the lib-commons OpenAPI/problem wrapper", "use the shared Huma wrapper", "standardize error responses on RFC 9457"
  • A service already carries a **local copy** of the wrapper (`internal/shared/adapters/openapi`, `shared/openapi`, `shared/humaerr`, a hand-rolled `huma.NewError` override) and should move onto the shared `lib-commons/v5/commons/net/http/{openapi,problem}`
  • A service is still on swaggo/swag (Swagger 2.0) and is migrating to Huma

Skip when

  • Service is not a Go project
  • Task does not involve an HTTP API / OpenAPI spec
  • The service genuinely needs a bespoke error envelope that is NOT RFC 9457 (rare; challenge it first)
  • Documentation-only or non-code task

You orchestrate. Agents implement. NEVER use Edit/Write/Bash on Go source files — all code changes go through `Task(subagent_type="ring:backend-go")`. TDD mandatory for implementation gates (RED → GREEN → REFACTOR). The orchestrator owns git (commit/push/PR/release) and NEVER commits without the user's go-ahead.

Architecture

The wrapper is **two packages** in `lib-commons/v5` (minimum **v5.6.0**):

| Alias | Import Path | Purpose | |-------|-------------|---------| | `openapi` | `github.com/LerianStudio/lib-commons/v5/commons/net/http/openapi` | `Config`, `New` (builds the `humafiber` API), `DeclareBearerAuth`, `ServeSpec` | | `problem` | `github.com/LerianStudio/lib-commons/v5/commons/net/http/problem` | `Install` (global `huma.NewError` override), `MapError`, `Detail`, `BaseURI` |

**`openapi.New(app, group, cfg)`** constructs the Fiber-v2 Huma API via `humafiber.NewV2WithGroup` (the default `humafiber.New` targets Fiber v3 — wrong major), strips Transformers/OnAddOperation/CreateHooks, and **clears the auto-mounted `/openapi.json` + `/docs`** (auto-mount is a latent production exposure — the spec must be served explicitly and gated). `Config{Title, Version, Description, Servers}`.

**`openapi.ServeSpec(app, api, logger, prefix, title)`** mounts `{prefix}/openapi.json`, `{prefix}/openapi.yaml`, `{prefix}/docs` (Scalar UI). It normalizes `prefix` (leading slash, no trailing) and HTML-escapes the docs-page title and spec URL. **Gate this behind the service's `Swagger.Enabled`** flag — never serve unconditionally.

**`openapi.DeclareBearerAuth(api)`** registers the `BearerAuth` security scheme *component* (`components.securitySchemes`) so `Security` references resolve — it does NOT attach the requirement to anything. **The scheme component alone advertises ZERO secured operations.** To make the spec say "auth required" you MUST also attach a requirement, one of:

  • **Global default** (covers every op in one line, drift-safe): `api.OpenAPI().Security = []map[string][]string{{"BearerAuth": {}}}` right after `openapi.New`.
  • **Per-operation**: `Security: []map[string][]string{{"BearerAuth": {}}}` on each `huma.Operation`.

`DeclareBearerAuth` without a requirement is the classic regression: a JWT-enforced API whose spec advertises every endpoint as public. **Public endpoints** (no auth middleware at runtime) must override the global default with an explicit empty requirement: `Security: []map[string][]string{}` on the operation — a *non-nil empty slice*, which Huma renders as `security: []` because `Operation.Security` marshals with `omitNil` (Go `json:"...,omitempty"` would drop it and silently re-inherit the global default). Getting this wrong makes the spec lie about which routes need a token — see also the constraint-as-validation caveat under Dependency facts.

**`problem.Install()`** is the crux. It is a `sync.Once` override of the process-global `huma.NewError`, so EVERY error Huma builds — domain errors via `MapError` and the framework's own validation/404/422 errors — becomes a `*problem.Detail`. Merge semantics:

  • **status >= 500**: body scrubbed to the static `"internal error"`, NO `errs` folded. This is the central info-leak guard — even a careless `huma.Error500(rawErr.Error())` cannot leak an internal cause.
  • **status < 500**: `msg` passes through and `errs` fold into `Errors[]` in order (skip nil, honor `huma.ErrorDetailer`) — native 422 field errors are preserved.

**`problem.MapError(err, codeOf, statusOf, fallbackCode)`** is the per-rail **flex seam** — the one place each service injects its own taxonomy:

  • `codeOf func(error) (code, msg string, ok bool)` — extract a domain code; `ok=false` → unrecognized → sanitized 500 carrying `fallbackCode`.
  • `statusOf func(code string) int` — map code → HTTP status.
  • `fallbackCode` — code carried when the error is nil/unrecognized.
  • A service WITH a code taxonomy passes real callbacks + a fallback (e.g. `"SPB-9000"`); a **bare** service passes an empty fallback and `code` is dropped (`omitempty`).

**`problem.Detail`** embeds `huma.ErrorModel` + an `omitempty` `Code`. Services that never set a code emit a bare RFC 9457 body; coded errors mapped through `MapError` carry the machine code in `Code` + a flat `Type` URI (`BaseURI + "/" + code`). `Type` is set **only** on the `MapError` coded path — framework-built errors (native 422/404 via `Install`) keep `Type` at `about:blank` with empty `Code`.

Dependency facts (do not re-litigate)

  • **huma version MUST match** what the pinned lib-commons build uses (`daniel
Read more
Ships withring

Proven engineering practices, enforced through skills. Ring is a comprehensive skills library and workflow system for AI agents that transforms how AI assistants approach software development.

Get the whole plugin

Other skills on ring.