| name | mdproof-implement-feature |
| description | Implement a feature from a spec file or description using TDD workflow. Use this skill whenever the user asks to: add a new CLI flag, implement a feature, build new functionality, create a new internal package, add an assertion type, extend the parser, or write Go code for mdproof. This skill enforces test-first development and proper package structure. If the request involves writing Go code and tests, use this skill — even if the user doesn't explicitly say "implement". |
| argument-hint | [spec-file-path | feature description] |
| targets | ["claude","codex"] |
Implement a feature following TDD workflow. $ARGUMENTS is a spec file path or a plain-text feature description.
Scope: This skill writes Go code and tests. It does NOT update CHANGELOG (use changelog after).
Workflow
Step 1: Understand Requirements
If $ARGUMENTS is a file path:
- Read the spec file
- Extract acceptance criteria and edge cases
- Identify affected packages
If $ARGUMENTS is a description:
- Search existing code for related functionality
- Identify the right package to extend
- Confirm scope with user before proceeding
Step 2: Identify Affected Files
List all files that will be created or modified:
internal/<package>/<feature>.go
internal/<package>/<feature>_test.go
cmd/mdproof/main.go
mdproof.go
Display the file list and continue. If scope is unclear, ask the user.
Step 3: Write Failing Tests First (RED)
Write tests in the appropriate internal/ package:
func TestFeature_BasicCase(t *testing.T) {
input := "test input"
result := SomeFunction(input)
if result != expected {
t.Errorf("got %v, want %v", result, expected)
}
}
For executor/runner tests that need shell execution:
func TestMain(m *testing.M) {
os.Setenv("MDPROOF_ALLOW_EXECUTE", "1")
os.Exit(m.Run())
}
Verify tests fail:
go test ./internal/<package> -run TestFeature_BasicCase
Step 4: Implement (GREEN)
Write minimal code to make tests pass:
- Follow existing patterns in
internal/
- Keep the dependency graph acyclic:
core → (nothing)
config → (nothing)
parser → core
assertion → core
executor → core, assertion
report → core
runner → core, parser, executor, config, report
sandbox → config
- New shared types go in
internal/core/types.go
- If adding public API, update
mdproof.go facade with type aliases
Verify tests pass:
go test ./internal/<package>
Step 5: Refactor and Verify
- Clean up code while keeping tests green
- Run full quality check:
make check
- Fix any formatting or lint issues
Step 6: Stage and Report
- List all created/modified files
- Confirm each acceptance criterion is met with test evidence
Package Reference
| Package | Purpose | Can import |
|---|
core | Shared types, constants | (nothing) |
config | mdproof.json loading | (nothing) |
parser | Markdown → Runbook | core |
assertion | Output matching | core |
executor | Bash session execution | core, assertion |
report | JSON + plain text output | core |
runner | Orchestrator | core, parser, executor, config, report |
sandbox | Auto-container provisioning | config |
Key Design Patterns
Facade Pattern (mdproof.go)
Root package re-exports types via aliases for clean public API:
type Step = core.Step
type Report = core.Report
Functions wrap internal calls:
func Run(r io.Reader, name string, opts RunOptions) (Report, error) {
return runner.Run(r, name, opts)
}
Container Safety
executor.IsContainerEnv() checks /.dockerenv or cgroup. Override with MDPROOF_ALLOW_EXECUTE=1. Tests must set this in TestMain.
Shell Session Persistence
Single bash process per runbook. Env vars persist via env file written after each step. This is core to mdproof's design — steps share state.
Rules
- Test-first — always write failing test before implementation
- Minimal code — only write what's needed to pass tests
- Follow patterns — match existing code style in each package
- No cycles — respect the dependency graph
- 3-strike rule — if a test fails 3 times after fixes, stop and report
- Spec ambiguity — ask the user rather than guessing