# Handoff Readiness

> Use when judging whether a publishable or reusable artifact — npm package, library, SDK, component kit, CLI binary, copy-paste/shadcn-style registry, or template — is ready to hand to real external users. Triggers include "is this publishable", "release ready", "handoff readiness", "can users actually install this", "audit package exports/types/peerDependencies", "pre-publish check", first public release, registry installability, or broken-consumer-import doubts.

- Skill: `b4r7x/handoff-readiness` (Agent Skill)
- Install (CLI): `npx skillmds@latest add b4r7x/handoff-readiness`
- Raw SKILL.md: https://api.skillmd.com/api/skills/b4r7x/handoff-readiness/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: b4r7x (https://skillmd.com/u/b4r7x)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/b4r7x/handoff-readiness

---


# Handoff Readiness

## Overview

Readiness is judged **per consumption channel**, not per repo: first detect what is being handed off and every way the project itself advertises consuming it, then walk each channel end-to-end as a cold external consumer would. An artifact that builds internally but breaks on `npm install` or on copy-paste is NOT ready.

## When to use / when NOT

**Use for:**
- Pre-publish audits of packages, SDKs, CLIs, component kits, templates
- Hosted/copy registries (shadcn-style) before exposing them to users
- "Is this ready / can we ship / can we hand this off" questions about reusable artifacts
- Verifying exports maps, type declarations, peer dependencies, files whitelists

**Do NOT use for:**
- Docs/content site deploy checks → see **docs-deploy-readiness**
- Internal cross-package reuse/duplication questions → see **reusability-audit**
- General code quality/architecture → see **code-audit**
- Deep security or a11y review → see **security-review**, **accessibility-compliance** (this skill only checks the *contract exists*)
- Running this audit across many packages with parallel agents → orchestrate via **convergence-loop**

## How it works

1. **DETECT the artifact and its channels.** Read, in order: root + per-package manifests (`package.json` or ecosystem equivalent — `pyproject.toml`, `Cargo.toml`, `go.mod`), `exports`/`bin`/`files`/`main`/`types` fields, build config (tsup/rollup/vite/esbuild/etc.), any committed registry JSON directories, README + docs install instructions. Classify the artifact as one or more of:
   - **Runtime package** (installed from a registry like npm, imported at runtime)
   - **Copy/source distribution** (code lands in the consumer's repo: shadcn-style registry, template, "copy this folder")
   - **Hosted registry** (committed JSON/index that a CLI or tool resolves; illustrative example only: `public/r/*.json` consumed by a shadcn-compatible or project-specific add CLI)
   - **CLI binary** (`bin` entry, installed globally or via `npx`/`pnpm dlx`)
   - **Template/starter** (cloned, then built by the consumer)
2. **Enumerate every ADVERTISED consumption path.** Only what the project documents counts as a channel — README install sections, docs "Installation" pages, registry index files, `bin` entries. Each advertised path becomes a row in the scorecard. An advertised-but-broken path is a blocker; an unadvertised path is out of scope.
3. **Apply the universal bar (a–e below) to each channel.** Skip dimensions that don't apply (e.g. exports-map checks for a pure copy distribution) and mark them N/A — never silently.
4. **Simulate the consumer journey statically per channel.** Follow the project's own install docs from zero: every referenced file, command, flag, alias, import specifier, and registry item must exist; no orphan imports (files importing siblings that the channel doesn't deliver); CSS/peer/env setup steps documented where required.
5. **Run mechanical verifiers where tools exist.** For npm-ecosystem packages, `publint` and `@arethetypeswrong/cli --pack` are the named 2026 baseline; also inspect `pnpm pack` (or `npm pack --dry-run`) output against what runtime actually needs. Use ecosystem equivalents elsewhere (`twine check`, `cargo package --list`). Prefer running tools over eyeballing.
6. **Score and report** using the Output format. Order blockers by what breaks the consumer first.

## Quality bar (universal, per channel)

### (a) Packaging correctness — runtime packages

| Check | Pass condition |
|---|---|
| Exports map | Every public subpath present; `types` condition **first** in every condition block |
| Declarations | Real `.d.ts` resolvable for **every** subpath (verify with attw, not by eye) |
| peerDependencies | Honest: frameworks/host libs as peers with ranges matching actual API usage; not duplicated in `dependencies` |
| sideEffects | Correct: `false` (or list) with CSS/global-effect files excepted |
| files whitelist | `pack` output contains everything runtime needs and nothing else (no src-only assumptions, no secrets) |
| Module format | ESM/CJS reality matches declarations; no `workspace:`/path-alias leakage into published output |
| Registry metadata | LICENSE, README (renders on the registry page), CHANGELOG, `repository` (+`directory` in monorepos), provenance where the ecosystem supports it |

### (b) Public API contract

- UI value controls: controlled/uncontrolled duality (`value`/`defaultValue`/`onChange`-style pairs); semantic callback names (`onOpenChange`, not `setOpen`/`handleClick2`)
- `ref` handled per current ecosystem norm; stable ids wired via a `useId`-style mechanism, not hardcoded
- The stable public surface is documented; internals are not reachable through public entries
- **No deprecated aliases before the first public release** — rename and update all consumers/docs/registry files instead
- React-specific depth → **react-senior-guide**

### (c) Division standard (file/component split)

- Detect the ecosystem norm the artifact targets (e.g. shadcn-style single file, per-part folders, flat one-file-per-component) and judge the split **by-responsibility and ecosystem-compatible**, not by personal taste
- For copy-distributed units: one install yields a complete, buildable unit; one obvious public entry per unit; remove/diff tooling treats the unit as the ownership boundary; docs list the installed file set

### (d) Installability end-to-end

- Each channel verified separately; copy mode must rewrite package-only imports to local relative paths
- No dangling references: every documented command, item name, file target, and import specifier resolves
- Peer setup (CSS imports, provider wrapping, config files) is part of the install docs, not tribal knowledge

### (e) A11y contract (UI libraries only)

- Each interactive component states its pattern contract (roles, keyboard table, focus behavior) somewhere consumable
- Deep verification is out of scope here → run **accessibility-compliance**

If the artifact handles user input, secrets, or network listeners (e.g. a CLI that starts a local server), also dispatch **security-review**.

## Quality bar (2026 snapshot)

Snapshot dated **2026-06-10**, distilled from researched sources. **Refresh via websearch with the current year before trusting any version number or tool claim below.**

**Packaging (npm ecosystem):**
- `publint` + `@arethetypeswrong/cli --pack` are the standard mechanical pair; "types condition first in every condition block" is the named rule
- ESM-only is the accepted default for new packages (Node ≥20.19/22.12 can `require()` ESM); dual CJS/ESM only for a concrete consumer need
- `sideEffects: false` with CSS files excepted; `files` verified via `pnpm pack`
- Changesets is the standard versioning tool for pnpm monorepos; npm provenance/trusted publishing (OIDC) increasingly expected for public packages
- 0.x semver is honest pre-release signaling (`^0.x` only patches within the minor), but README/LICENSE/CHANGELOG/`repository.directory` are still required

**Component libraries (React line):**
- `forwardRef` is deprecated; ref-as-prop is the norm (React 19+). New library code should not contain `forwardRef`
- React Compiler v1.0: npm libraries pre-compile before publishing; **copy-distributed** source still follows "no defensive `useMemo`/`useCallback`/`memo`" since consumer compiler use is unknown
- Consumers assume: controlled/uncontrolled pairs, state exposed as data-attributes (`data-state`, `data-slot`) for Tailwind selectors, composition via `asChild` or a `render` prop, `useId` for ARIA wiring
- Tailwind v4 libs: ship uncompiled source CSS consumers `@import`; never ship `@import "tailwindcss"` in dist CSS; document the `@source "../node_modules/<pkg>"` directive (v4 skips node_modules scanning)
- `"use client"` must survive the build (bundlers strip directives by default) for RSC-host compatibility; peer ranges honest (`>=19` for ref-as-prop, `>=19.2` for `useEffectEvent`/`Activity`)

**File division / registry distribution:**
- The shadcn registry schema fully supports multi-file items (`files[]` with per-file `path`, `type`, `target`; target placeholders preserve subdirectories) — both single-file (Radix/React Aria/classic shadcn) and per-part folders (Base UI, now a supported shadcn layer) are mainstream
- A multi-file-but-ecosystem-compatible unit MUST: (1) declare every file in the registry item with correct targets so one add is complete and buildable; (2) rewrite package-only imports to local relative paths in copy mode; (3) keep one obvious entry (`index.ts` per unit); (4) have remove/diff treat the unit as the ownership boundary; (5) list the installed file set in docs
- Split by responsibility (part/hook/variants/types), never by line-count ritual — copy readers must still be able to skim
- Registry index (`registry.json`) + per-item (`registry-item.json`); `dependencies` (npm) vs `registryDependencies` (other items); namespaced registries and CLI/MCP installs are current norms
- Copy/registry "published" = the committed public registry JSON is the contract; it must install cleanly via **every** advertised path — same bar as npm publish validation

## Output

Produce exactly this report shape:

```markdown
# Handoff Readiness — <artifact/repo name>

Detected artifact: <classification, e.g. "npm package + copy registry + CLI binary">
Detected channels (advertised):
- <channel> — evidence: <file path(s)>

## Per-channel scorecard
| Channel | Packaging | API contract | Division | Installability | A11y contract | Verdict |
|---|---|---|---|---|---|---|
| <channel> | PASS/FAIL/N/A | PASS/FAIL/N/A | PASS/FAIL/N/A | PASS/FAIL/N/A | PASS/FAIL/N/A | READY / BLOCKED |

## Blockers (ordered: what breaks the consumer first)
1. [BLOCKER] <channel> — <what a real consumer hits> — <file:line> — <concrete fix>

## Warnings (non-blocking)
1. [WARN] <channel> — <issue> — <file:line>

## Verification run
- <tool/command> → <result>  (e.g. publint, attw --pack, pnpm pack inspection; or "not available: <why>")

## Verdict: READY | NOT READY (<n> blockers)
```

A channel is **BLOCKED** if any consumer-facing failure exists on it; the artifact is **NOT READY** if any advertised channel is blocked.

## Common mistakes

- Auditing only the npm channel when copy/registry/CLI paths are also advertised — every documented path is a contract
- Eyeballing the exports map instead of running publint/attw; the map can look right and still resolve wrong types
- Treating a green internal monorepo build as proof consumers can build — `workspace:` protocol, tsconfig path aliases, and sibling-source imports all vanish or break outside the repo
- Judging file division by taste instead of the detected ecosystem norm
- Marking READY with undocumented peer/CSS/provider setup ("it works in our app" is not install docs)
- Hardcoding one project's specifics as required checks — a Hono localhost server, a `dgadd`-style CLI, or `--tui-*` tokens are illustrative shapes to *detect*, never assumed to exist
- Skipping the a11y-contract row for UI kits because "accessibility is a separate audit" — the contract's *existence* is part of handoff
- Re-explaining sibling skills inline instead of dispatching to **accessibility-compliance**, **security-review**, **react-senior-guide**, or **reusability-audit**
- Trusting the dated snapshot above without refreshing it against the current year

