From Toge Design Agent
Developer handoff pass — cleans and restructures prototype code for production readiness. Splits oversized components, extracts composables, types all props/emits, removes prototype artifacts (mock delays, console.logs, placeholder comments), and verifies file structure. Trigger phrases: "ready for handoff", "clean up the code", "handoff pass", "production ready", "prepare for dev", "clean for frontend".
How this skill is triggered — by the user, by Claude, or both
Slash command
/toge:handoffThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill turns a working prototype into clean, modular, handoff-ready code that a frontend developer can build production features from without needing to untangle prototype shortcuts.
This skill turns a working prototype into clean, modular, handoff-ready code that a frontend developer can build production features from without needing to untangle prototype shortcuts.
Input: A completed prototype/ directory
Output: The same directory, refactored — no behavior changes, no new features
If something needs to be redesigned, surface it as a comment — don't fix it here.
Before changing anything, read all files in prototype/. Build a mental map of:
Split any component where any of these is true:
| Signal | Action |
|---|---|
| Template exceeds ~80 lines | Extract the largest independent section into a child component |
| Component handles both data orchestration AND rendering | Separate into a container + presentational pair |
| A template block is repeated 2+ times | Extract into a shared component |
| A screen has 3+ distinct UI sections | Each section gets its own component |
Route/screen-level components (files in screens/) must stay thin — app shell, layout, and feature composition only. Full feature implementations belong in child components.
Naming: PascalCase for components, one clear responsibility per name (PayslipCard, not PayslipCardAndActions).
After splitting, verify the parent passes props down and receives events up. No side effects in presentational components.
Extract into a composable when:
<script setup>Composable rules:
composables/use[Feature].js (or .ts if the project uses TypeScript)return { items, isLoading, selectedItem, select }refExample extraction:
// Before: inline in screen component
const employees = ref([{ id: 1, name: 'Maria Santos' }])
const isLoading = ref(false)
async function loadEmployees() { ... }
// After: composables/useEmployees.js
export function useEmployees() {
const employees = ref([{ id: 1, name: 'Maria Santos' }])
const isLoading = ref(false)
async function load() { ... }
return { employees, isLoading, load }
}
Every component that receives props or emits events must declare them explicitly.
Props:
<script setup>
// Typed with JSDoc (JS projects)
const props = defineProps({
employee: { type: Object, required: true },
isSelected: { type: Boolean, default: false }
})
// Or TypeScript interface (TS projects)
const props = defineProps<{
employee: Employee
isSelected?: boolean
}>()
</script>
Emits:
<script setup>
const emit = defineEmits(['select', 'dismiss'])
// or TypeScript:
const emit = defineEmits<{
select: [employee: Employee]
dismiss: []
}>()
</script>
Flag any defineProps without types, any $emit calls without a matching defineEmits, and any prop passed via v-bind="$attrs" without inheritAttrs: false.
Scan every file and remove or flag:
| Artifact | What to do |
|---|---|
console.log(...) | Remove |
// TODO, // FIXME, // HACK comments | Remove or resolve — none should survive handoff |
setTimeout used only to simulate latency | Keep if it controls a visible loading state; remove if it serves no UX purpose |
Hardcoded magic numbers (e.g. setTimeout(fn, 500)) | Extract to a named constant at the top of the file: const SUBMIT_DELAY_MS = 500 |
| Commented-out code blocks | Remove |
Unused import statements | Remove |
Unused ref/reactive declarations | Remove |
Inline style hacks (style="...") | Replace with Tailwind classes or token classes |
The final prototype/ directory must follow this structure. Rename or move files that don't fit.
prototype/
├── main.js ← Stack context comment on line 1
├── App.vue ← Router root + persistent layout only
├── router.js ← Named routes, one per screen
├── stores/ ← Pinia stores for cross-screen state only
│ └── use[Feature]Store.js
├── composables/ ← All mock data + reusable logic
│ └── use[Feature].js
├── components/ ← Shared UI used in 2+ screens
│ └── [Feature]/ ← Group by feature, not by type
│ └── [ComponentName].vue
└── screens/ ← One file per route, thin composition surfaces
├── 01-[screen].vue
└── ...
Checks:
App.vue contains no feature logic — only <RouterView> and persistent shellrouter.js uses named routes with lazy-loaded screen importsref or computed — not a plain variablecomputed, not manual watcherswatch where computed would do the same jobreactive on a single primitive — use refBefore the code quality checklist, verify that the prototype covers everything the product unit required. This is the "did we build it all" gate.
Read the source product unit (UAC, story doc, or feature spec) that was used in Phase 0 to generate the screen spec. Then trace each requirement against the prototype:
| Requirement | Source | Screen / Component | Status |
|---|---|---|---|
| Actor: PSI can view payslip summary | UAC §2.1 | screens/01-payslip-summary.vue | ✅ implemented |
| Actor: PSI can flag a discrepancy | UAC §2.3 | screens/01-payslip-summary.vue → FlagDiscrepancyDrawer.vue | ✅ implemented |
| State: Empty state when no payslips exist | UAC §2.1 | components/Payslip/PayslipList.vue | ⚠️ missing |
Coverage rules:
⏸️ deferred.Outcome:
❌ gap. Flag it in the check-in as a blocker — the prototype is incomplete and the gap must be resolved before handoff.⚠️ missing state. Flag it as a non-blocking gap — include a recommendation for the developer.⏸️ deferred and note which sprint/Bolt it belongs to.Produce the Coverage Table as part of the check-in output.
Run through this before declaring handoff done:
defineProps are typeddefineEmits are declaredconsole.log, no commented-out blocks, no // TODOstyle="..." hacksApp.vue is a thin shellrouter.js uses named lazy routes❌ gap items)After the pass, present in order:
❌ gap items as blockers.Then use AskUserQuestion:
"Handoff pass complete. [X/Y requirements covered — N gaps flagged / all requirements covered.] Here's what was refactored: [list]. Any of these changes need a second look before this goes to the dev team?"
If there are ❌ gap items, the question becomes:
"Handoff pass found [N] unimplemented requirements from the product unit: [list]. These need to be designed and implemented before this is dev-ready. Want me to address them now?"
After the check-in, call skills/learnings/SKILL.md with the handoff output. Pass the list of splits, extractions, and structural decisions. The skill extracts pattern and convention entries — component boundaries that worked, composable patterns, structural conventions to repeat. Report what was captured inline.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub micserr/sprout-design-agent --plugin toge