skills/harden/SKILL.md
Production resilience pass. Addresses error states, text overflow, i18n prep, edge cases, loading states, and input validation. Use when asked to "harden this", "make it production ready", "handle edge cases", or before shipping UI to real users.
npx skillsauth add howells/arc hardenInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
<tool_restrictions>
AskUserQuestion — Preserve the one-question-at-a-time interaction pattern for user decisions such as applying fixes or keep/remove choices. In Claude Code, use the tool. In Codex, ask one concise plain-text question at a time unless a structured question tool is actually available in the current mode. Do not narrate missing tools or fallbacks to the user.EnterPlanMode — BANNED. Execute phases below directly.ExitPlanMode — BANNED. You are never in plan mode.
</tool_restrictions><arc_runtime> Arc-owned files live under the Arc install root for full-runtime installs.
Set ${ARC_ROOT} to that root and use ${ARC_ROOT}/... for Arc bundle files such as
references/, disciplines/, agents/, templates/, scripts/, and rules/.
Project-local files stay relative to the user's repository. </arc_runtime>
Strengthen UI against real-world usage. Designs that only work with perfect data aren't production-ready.
Announce at start: "I'm using the harden skill to make this production-resilient."
<important> **This skill is user-interactive. Do NOT spawn agents.** Hardening decisions need context — what's likely vs. paranoid, what's worth the complexity. </important><required_reading> Read these using the Read tool:
rules/interface/forms.md — Form behavior and validationrules/interface/interactions.md — Interactive states, destructive actionsrules/interface/content-accessibility.md — Accessible content${ARC_ROOT}/references/touch-targets.md — Hit target expansion, minimum sizes, pseudo-element technique${ARC_ROOT}/references/ux-laws.md — Postel's Law (input tolerance), Hick's Law (option overload), Cognitive Load
</required_reading>Read all files for the target component/page. Identify:
Work through each hardening dimension:
Every text element needs an overflow strategy:
<!-- Single line — truncate -->
<p class="truncate">Long text gets ellipsis...</p>
<!-- Multi-line — clamp -->
<p class="line-clamp-3">Shows 3 lines then ellipsis...</p>
<!-- Headings — balance wrapping -->
<h1 class="text-balance">Heading wraps elegantly</h1>
<!-- Body — pretty wrapping -->
<p class="text-pretty">Body text avoids orphans</p>
<!-- Flex children — prevent overflow -->
<div class="min-w-0"><!-- Required in flex to allow truncation --></div>
<!-- URLs and long words -->
<p class="break-all">superlongdomainname.com/path/to/thing</p>
Check:
min-w-0 where neededbreak-all or break-words)Every data-driven view needs an empty state:
<!-- Not just "No items" — provide context and action -->
<div class="flex flex-col items-center gap-4 py-12 text-center">
<p class="text-gray-500">No projects yet</p>
<p class="text-sm text-gray-400">Create your first project to get started.</p>
<button class="...">Create project</button>
</div>
Check:
Every async operation needs loading feedback:
<!-- Skeleton loading — preferred over spinners -->
<div class="animate-pulse space-y-4">
<div class="h-4 w-3/4 rounded bg-gray-200"></div>
<div class="h-4 w-1/2 rounded bg-gray-200"></div>
</div>
<!-- Button loading — disable + spinner + keep label -->
<button disabled class="disabled:opacity-50" aria-busy="true">
<Spinner class="size-4 animate-spin" />
Save changes
</button>
<!-- Inline loading -->
<div aria-busy="true" aria-live="polite">Loading...</div>
Check:
disabled, aria-busy)aria-busy and aria-live for screen readersEvery operation that can fail needs error handling:
<!-- Inline field error -->
<input aria-invalid="true" class="border-red-500 focus:ring-red-500" />
<p class="mt-1 text-sm text-red-500" role="alert">Email address is required</p>
<!-- Page-level error with retry -->
<div class="flex flex-col items-center gap-4 py-12 text-center" role="alert">
<p class="text-red-500">Something went wrong loading your data.</p>
<button onclick="retry()">Try again</button>
</div>
Error messages must:
Check:
aria-invalid and role="alert"Even if not translating yet, prepare the UI:
<!-- Use logical properties (RTL-safe) -->
<div class="ms-4 me-2 ps-3 pe-3"> <!-- Not ml-4 mr-2 pl-3 pr-3 -->
<!-- Budget 30-40% more space for translations -->
<!-- "Save" (EN) → "Speichern" (DE) → "Enregistrer" (FR) -->
<button class="min-w-[120px]">Save</button> <!-- Don't constrain to exact content width -->
Use the Intl API for dates, numbers, currency:
// Not: "March 5, 2026" or toLocaleDateString()
new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(date)
new Intl.NumberFormat('en', { style: 'currency', currency: 'USD' }).format(price)
Check:
ms-*, me-*, ps-*, pe-* not ml-*, mr-*)Intl APITest mentally with extreme inputs:
| Scenario | What breaks? | |----------|-------------| | 0 items | Empty state needed | | 1 item | Singular/plural text? Layout with single child? | | 1,000+ items | Pagination or virtual scroll needed? | | 500-char user name | Text overflow? Layout break? | | Slow network (3G) | Loading state needed? Optimistic UI? | | Offline | Error handling? Cache? | | Double-click submit | Duplicate prevention? | | Paste into input | Allowed? Sanitized? | | Browser back | State preserved? Scroll restored? |
Check:
disabled after click, idempotency keys)Validate client-side for UX, server-side for security:
<!-- Set constraints with HTML attributes -->
<input
type="email"
required
maxlength="255"
autocomplete="email"
class="..."
/>
<!-- Accept free text, validate after -->
<!-- NEVER block typing -->
<!-- MUST allow submitting incomplete forms to surface validation -->
Check:
type for keyboard (email, tel, url, number)autocomplete and name for login/address formsmaxlength on text inputsaria-describedbyPresent findings grouped by impact:
For each finding: describe the issue, show the fix with Tailwind classes, then ask for approval before applying:
AskUserQuestion:
question: "Apply this fix?"
header: "Hardening Fix"
options:
- label: "Apply"
description: "Apply this fix now"
- label: "Skip"
description: "Skip this fix and move to the next finding"
- label: "Apply all"
description: "Apply this and all remaining fixes without asking"
If the user selects "Apply all", apply all remaining fixes without further prompts.
After fixes:
<arc_log>
After completing this skill, append to the activity log.
See: ${ARC_ROOT}/references/arc-log.md
Entry: /arc:harden — [Component/page] hardened ([# issues found, # fixed])
</arc_log>
<success_criteria> Harden is complete when:
development
Create, review, or revise a concise project vision document that captures what a project is, who it is for, why it exists, success criteria, constraints, non-goals, and decision principles. Use when starting a new project, clarifying product direction, aligning a codebase for future agent work, defining a north star, or turning a vague idea into docs/vision.md.
tools
Use when starting any conversation - establishes Arc's skill routing, instruction priority, and bootstrap rules
development
Characterization testing and safety-net backfill for existing code. Use when legacy, under-tested, or risky code needs tests before a refactor, bug fix, or behavior change. Captures current behavior through public interfaces, identifies coverage gaps, and adds focused unit, integration, or E2E tests without replacing TDD implementation workflows.
testing
Run expert review on a plan, spec, or implementation approach with parallel reviewer agents. Presents findings as Socratic questions. Use when asked to "review the plan", "check this approach", or before implementation to validate architectural decisions. Optional argument: reviewer name (e.g., `/arc:review daniel-product-engineer`)