hole-driven-development-core
Scannednpx machina-cli add skill jhhuh/hole-driven-development-skill/hole-driven-development-core --openclawHole Driven Development — Core
Overview
Implement top-down by decomposition. Start with the outermost structure, leave holes for unknown parts, then fill each hole iteratively — possibly introducing smaller holes — until none remain.
Core principle: Never write a complete implementation in one pass. Decompose first, fill later.
The Loop
1. Receive intention (type signature, spec, or description)
2. Write skeleton to the file with HOLES for each unknown sub-problem
3. Pick the most constrained hole (fewest valid fills)
4. Determine what the hole needs (type, contract, inputs available)
5. Fill ONE hole in the file — introduce sub-holes if the fill is itself complex
6. VERIFY the fill against previously filled code:
- Shared mutable state: is access synchronized?
- Lifecycle: are acquire/release scopes matched across holes?
- Error/cancel paths: do they clean up resources from other holes?
If any check fails, fix before proceeding.
7. Repeat from 3 until no holes remain
8. REVIEW-ALL: before declaring done, re-read the complete implementation:
- State transitions that span multiple fills
- Resource acquired in one fill, released in another
- Error paths that cross fill boundaries
- Loop invariants depending on multiple fills
Fix any systemic bug the per-hole VERIFY could not catch.
Holes must be visible. Write holes to the file, not just in your reasoning. Each iteration of the loop edits the file — the human should see the skeleton evolve in their editor.
One hole per iteration. Fill exactly one hole, then reassess. Do not batch-fill.
Most constrained first. When multiple holes exist, fill the one with the narrowest contract — it has the fewest possible implementations, so you're least likely to get it wrong.
When ambiguous — multiple equally valid fills — STOP and ask the human.
When NOT to decompose further. Some algorithms are inherently monolithic — dual-cursor walks, coroutine-style loops, complex state machines with tightly coupled transitions. If the state cannot be cleanly partitioned across hole boundaries, keep it as a single hole with internal comments for structure rather than splitting into sub-holes that introduce bugs at the seams.
Success Criterion
No holes remain, the code satisfies the original intention, the REVIEW-ALL pass found no systemic bugs, and no cross-hole interaction bugs remain.
What This Skill Does NOT Cover
- How to determine what a hole needs — that depends on the extending skill:
- Compiler-feedback loop:
hole-driven-development(runs compiler, reads diagnostics) - Iterative reasoning:
hole-driven-development-iterative-reasoning(Claude reasons about contracts)
- Compiler-feedback loop:
Red Flags — STOP
- Writing a complete implementation without first creating holes
- Keeping holes in your head instead of writing them to the file
- Filling multiple holes in a single step
- Skipping hole analysis ("I already know the answer")
- Writing the final code directly and claiming you "decomposed mentally"
- Creating artificial decomposition for trivial one-liners (1-2 lines of obvious code)
- Skipping the VERIFY step after filling ("it's obviously correct in context")
If you catch yourself doing any of these: STOP. Delete. Start with holes in the file.
Source
git clone https://github.com/jhhuh/hole-driven-development-skill/blob/master/skills/hole-driven-development-core/SKILL.mdView on GitHub Overview
Hole Driven Development — Core lets you implement functions or modules by decomposing into holes. Start with the outer structure, leave holes for unknown parts, and fill iteratively from the most constrained to the least. Never write a complete implementation in one pass; decompose first, fill later.
How This Skill Works
Begin from an intention (type signature, spec, or description), write a skeleton with HOLES for unknown sub-problems, and then repeatedly choose the most constrained hole to fill. After each fill, verify cross-hole aspects (shared state, lifecycle, error paths) before proceeding. Continue until no holes remain, then run a final REVIEW-ALL to ensure coherence and surface bugs across holes.
When to Use It
- Starting a new function or module and sketching the design with holes before implementation.
- Implementing complex logic that can be decomposed into smaller subparts with typed holes.
- Refactoring a monolithic function into a top-down, hole-driven structure.
- Collaborating with teammates by exposing holes for discussion and incremental progress.
- When you need explicit cross-hole verification (state, resource lifecycle, and error paths) before completion.
Quick Start
- Step 1: Receive intention (type signature, spec, or description) and write a skeleton with HOLES for each unknown sub-problem.
- Step 2: Pick the most constrained hole, determine its needs, and fill it (introduce sub-holes if needed).
- Step 3: Verify the fill against existing code (state, lifecycle, error paths); repeat from Step 2 until no holes remain, then perform a final REVIEW-ALL.
Best Practices
- Write holes visibly in the file; never rely on memory.
- Fill exactly one hole per iteration and reassess before the next fill.
- Always pick the most constrained hole first (fewest valid fills).
- Use the VERIFY step to check cross-hole interactions: shared state, lifecycles, and error paths.
- Finish with a thorough REVIEW-ALL to catch systemic bugs and cross-hole issues.
Example Use Cases
- Implement a new API client by skeletonizing each endpoint as a hole and filling them one at a time.
- Decompose a data-processing pipeline into parsing, transformation, and I/O holes to reason about interfaces.
- Refactor a large function into smaller holes to isolate concerns and simplify testing.
- Add resource management holes to ensure acquire/release scopes are balanced across fills.
- Introduce hole-driven templates in a legacy codebase to facilitate pair programming and incremental migration.