Adds the /evaluate-library skill runbook at .claude/skills/evaluate-library/ with SKILL.md (8-filter + 3-prompt protocol, collect-cheap-skip-expensive ordering, trace-write step, skip sentinel), POLICY.md (ADR-022 summary ≤2 pages), TRACE-TEMPLATE.md (complete YAML frontmatter + 11 headings in order), and EXAMPLES/ with one approved (clsx) and one rejected (trpc-to-openapi, named-consumer: fail) worked trace. Updates session-start.sh to surface the skill in session pointers. The skill is auto-registered by the harness on SKILL.md creation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
4.3 KiB
Markdown
92 lines
4.3 KiB
Markdown
---
|
||
package: clsx
|
||
version: "^2.1.1"
|
||
tier: feature
|
||
decision: approved
|
||
date: 2026-05-14
|
||
deciders: [danijel, claude-sonnet-4-6]
|
||
adr: null
|
||
filter-results:
|
||
license: MIT
|
||
types: native
|
||
maintenance: active
|
||
boundary-fit: pass
|
||
shadow-check: pass
|
||
eu-residency: n/a
|
||
cve-scan: clean
|
||
named-consumer: pass
|
||
verification-commands:
|
||
- node -e "const p = JSON.parse(require('fs').readFileSync('./node_modules/clsx/package.json','utf8')); console.log(p.license)"
|
||
- ls node_modules/clsx/dist/clsx.d.ts
|
||
- npm view clsx time.modified
|
||
- pnpm audit --audit-level=moderate
|
||
accepted-cves: []
|
||
---
|
||
|
||
## Filter: license
|
||
|
||
<!-- Result: MIT -->
|
||
|
||
`package.json` declares `"license": "MIT"`. MIT is on the allowlist. Pass.
|
||
|
||
## Filter: types
|
||
|
||
<!-- Result: native -->
|
||
|
||
`clsx` ships its own `.d.ts` declarations at `dist/clsx.d.ts`. No `@types/clsx` package needed.
|
||
The TypeScript surface covers the full public API (`ClassValue`, overloads). Pass.
|
||
|
||
## Filter: maintenance
|
||
|
||
<!-- Result: active -->
|
||
|
||
Last npm release: `2.1.1` published 2024-02-06 (15 months ago at evaluation date — within the 18-month threshold). GitHub shows open PR/issue activity within the last 3 months. The library is small, intentionally stable, and actively maintained. Pass.
|
||
|
||
## Filter: boundary-fit
|
||
|
||
<!-- Result: pass -->
|
||
|
||
`clsx` is a pure string-concatenation utility. It imports nothing from Node.js or browser globals; it has zero transitive dependencies. Adding it to `packages/navigation` as a `feature`-tagged package introduces no boundary violations under ADR-006 or ADR-010. It does not import `@sentry/*`, `@opentelemetry/*`, or any core-reserved vendor SDK. Pass.
|
||
|
||
## Filter: shadow-check
|
||
|
||
<!-- Result: pass -->
|
||
|
||
The locked workspace must-haves are: `zod`, `inversify`, `payload`, `@trpc/server`, `superjson`, `reflect-metadata`. None of these perform CSS class-name composition. There is no existing utility in the workspace for this purpose. Pass.
|
||
|
||
## Filter: eu-residency
|
||
|
||
<!-- Result: n/a -->
|
||
|
||
`clsx` is a pure in-process string utility. It performs no network calls, transmits no user data, and has no SaaS endpoint. EU residency filter does not apply.
|
||
|
||
## Filter: cve-scan
|
||
|
||
<!-- Result: clean -->
|
||
|
||
`pnpm audit --audit-level=moderate` returns 0 vulnerabilities for `clsx@2.1.1` at evaluation time. No accepted advisories.
|
||
|
||
## Filter: named-consumer
|
||
|
||
<!-- Result: pass -->
|
||
|
||
Named consumer: `packages/navigation/src/ui/components/navigation-menu.tsx` — the `NavigationMenuLink` component must compute conditional class names for the active/inactive link state. Without `clsx` this is implemented as a ternary chain that becomes unreadable past three conditions. The component exists today; this is not a hypothetical future use case.
|
||
|
||
Secondary consumer: `packages/navigation/src/ui/components/mobile-nav.tsx` — open-state drawer overlay class computation. Both components are blocked on this adoption.
|
||
|
||
## Prompt: replaces
|
||
|
||
Replaces inline ternary chains like `` `base-class ${isActive ? 'active' : ''} ${isDisabled ? 'disabled' : ''}` ``. No library is being retired — this is a first-time adoption of a class-composition utility. No parallel adoption risk.
|
||
|
||
## Prompt: migration-cost-out
|
||
|
||
**Mechanical.** `clsx` is called only at the component leaf level. Removal means replacing `clsx(...)` calls with equivalent template-literal ternaries — a mechanical sed-style refactor bounded to the `packages/navigation/src/ui/` subtree. No data format dependencies, no vendor lock-in, no protocol coupling.
|
||
|
||
## Prompt: alternatives-considered
|
||
|
||
1. **`classnames`** — functional equivalent, MIT, widely used. Rejected in favour of `clsx` because `clsx` is the successor written by the same author with better TypeScript support and 2× faster benchmarks at comparable bundle size (330 B vs 440 B minzipped). `classnames` would also pass all eight filters; `clsx` is strictly preferable.
|
||
|
||
2. **Inline ternary chains (no library)** — the current approach. Adequate for one or two conditions; degrades rapidly past three. The `navigation-menu` component already has four conditional classes; this is the threshold where a utility library pays for itself. Rejected as the status quo.
|
||
|
||
3. **`tailwind-merge`** — superset of `clsx` that also de-duplicates conflicting Tailwind classes. Overkill for this use case (navigation components use a small, non-conflicting class set). Higher migration cost out (data-format dependency on Tailwind class semantics). Deferred.
|