feat: add verification and debugging workflow skills

This commit is contained in:
alex
2026-03-11 11:27:40 +00:00
parent f7a1aac68b
commit 014016328d
7 changed files with 497 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
---
name: systematic-debugging
description: Use when encountering bugs, test failures, or unexpected behavior before proposing fixes
permalink: opencode-config/skills/systematic-debugging/skill
---
# Systematic Debugging
## Overview
Random fix attempts create churn and often introduce new issues.
**Core principle:** always identify root cause before attempting fixes.
## The Iron Law
```
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
```
If Phase 1 is incomplete, do not propose or implement fixes.
## When to Use
Use for any technical issue:
- Test failures
- Unexpected runtime behavior
- Build or CI failures
- Integration breakages
- Performance regressions
Use this especially when:
- You are under time pressure
- A "quick patch" seems obvious
- Previous fix attempts did not work
- You do not yet understand why the issue occurs
## Four-Phase Process
Complete each phase in order.
### Phase 1: Root-Cause Investigation
1. Read error messages and stack traces fully.
2. Reproduce the issue reliably with exact steps.
3. Check recent changes (code, config, dependency, environment).
4. Gather evidence at component boundaries (inputs, outputs, config propagation).
5. Trace data flow backward to the original trigger.
For deeper tracing techniques, see `root-cause-tracing.md`.
### Phase 2: Pattern Analysis
1. Find similar working code in the same repository.
2. Compare broken and working paths line by line.
3. List all differences, including small ones.
4. Identify required dependencies and assumptions.
### Phase 3: Hypothesis and Minimal Testing
1. State one concrete hypothesis: "X is failing because Y".
2. Make the smallest possible change to test only that hypothesis.
3. Verify result before making any additional changes.
4. If the test fails, form a new hypothesis from new evidence.
### Phase 4: Fix and Verify
1. Create a minimal failing reproduction (automated test when possible).
2. Implement one fix targeting the identified root cause.
3. Verify the issue is resolved and no regressions were introduced.
4. If fix attempts keep failing, stop and reassess design assumptions.
## Red Flags (Stop and Restart at Phase 1)
- "Let me try this quick fix first"
- "Ill batch several changes and see what works"
- "It probably is X"
- Proposing solutions before tracing the data flow
- Continuing repeated fix attempts without new evidence
## Supporting Techniques
Use these companion references while executing this process:
- `root-cause-tracing.md` — trace failures backward through the call chain
- `condition-based-waiting.md` — replace arbitrary sleeps with condition polling
- `defense-in-depth.md` — add layered validation so recurrence is harder
## Related Skills
- `test-driven-development` — build minimal failing tests and iterate safely
- `verification-before-completion` — confirm behavior end-to-end before claiming done

View File

@@ -0,0 +1,68 @@
---
title: condition-based-waiting
type: note
permalink: opencode-config/skills/systematic-debugging/condition-based-waiting
---
# Condition-Based Waiting
## Overview
Arbitrary sleep durations create flaky tests and race conditions.
**Core principle:** wait for the condition that proves readiness, not a guessed delay.
## When to Use
Use this when:
- Tests rely on `sleep` or fixed `setTimeout` delays
- Asynchronous operations complete at variable speeds
- Tests pass locally but fail in CI or under load
Avoid arbitrary waits except when explicitly validating timing behavior (for example, debounce intervals), and document why timing-based waiting is necessary.
## Core Pattern
```ts
// ❌ Timing guess
await new Promise((r) => setTimeout(r, 100));
// ✅ Condition wait
await waitFor(() => getState() === 'ready', 'state ready');
```
## Generic Helper
```ts
async function waitFor<T>(
condition: () => T | false | undefined | null,
description: string,
timeoutMs = 5000,
pollMs = 10
): Promise<T> {
const started = Date.now();
while (true) {
const result = condition();
if (result) return result;
if (Date.now() - started > timeoutMs) {
throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
}
await new Promise((r) => setTimeout(r, pollMs));
}
}
```
## Practical Guidance
- Keep polling intervals modest (for example, 10ms) to avoid hot loops.
- Always include a timeout and actionable error message.
- Query fresh state inside the loop; do not cache stale values outside it.
## Common Mistakes
- Polling too aggressively (high CPU, little benefit)
- Waiting forever without timeout
- Mixing arbitrary delays and condition checks without rationale

View File

@@ -0,0 +1,64 @@
---
title: defense-in-depth
type: note
permalink: opencode-config/skills/systematic-debugging/defense-in-depth
---
# Defense in Depth
## Overview
A single validation check can be bypassed by alternate paths, refactors, or test setup differences.
**Core principle:** add validation at multiple layers so one missed check does not recreate the same failure.
## Layered Validation Model
### Layer 1: Entry Validation
Reject obviously invalid input at boundaries (CLI/API/public methods).
### Layer 2: Business-Logic Validation
Re-validate assumptions where operations are performed.
### Layer 3: Environment Guards
Block dangerous operations in sensitive contexts (for example, test/runtime safety guards).
### Layer 4: Diagnostic Context
Emit enough structured debug information to support future root-cause analysis.
## Applying the Pattern
1. Trace real data flow from entry to failure.
2. Mark all checkpoints where invalid state could be detected.
3. Add targeted validation at each relevant layer.
4. Verify each layer can catch invalid input independently.
## Example Shape
```ts
function createWorkspace(path: string) {
// Layer 1: entry
if (!path || path.trim() === '') {
throw new Error('path is required');
}
// Layer 2: operation-specific
if (!isPathAllowed(path)) {
throw new Error(`path not allowed: ${path}`);
}
}
async function dangerousOperation(path: string) {
// Layer 3: environment guard
if (process.env.NODE_ENV === 'test' && !isSafeTestPath(path)) {
throw new Error(`refusing unsafe path in test mode: ${path}`);
}
// Layer 4: diagnostic context
console.error('operation context', { path, cwd: process.cwd(), stack: new Error().stack });
}
```
## Key Outcome
Root-cause fixes prevent recurrence at the origin. Layered validation reduces the chance that adjacent paths can reintroduce the same class of bug.

View File

@@ -0,0 +1,66 @@
---
title: root-cause-tracing
type: note
permalink: opencode-config/skills/systematic-debugging/root-cause-tracing
---
# Root-Cause Tracing
## Overview
Many bugs appear deep in a stack trace, but the origin is often earlier in the call chain.
**Core principle:** trace backward to the original trigger, then fix at the source.
## When to Use
Use this when:
- The symptom appears far from where bad input was introduced
- The call chain spans multiple layers or components
- You can see failure but cannot yet explain origin
## Tracing Process
1. **Capture the symptom clearly**
- Exact error text, stack frame, and context.
2. **Find immediate failure point**
- Identify the exact operation that throws or misbehaves.
3. **Walk one frame up**
- Determine who called it and with which values.
4. **Repeat until source**
- Continue tracing callers and values backward until you find where invalid state/data originated.
5. **Fix at source**
- Correct the earliest trigger rather than patching downstream symptoms.
## Instrumentation Tips
When manual tracing is hard, add targeted instrumentation before the risky operation:
```ts
const stack = new Error().stack;
console.error('debug context', {
input,
cwd: process.cwd(),
envMode: process.env.NODE_ENV,
stack,
});
```
Guidelines:
- Log before failure-prone operations, not after.
- Include values that influence behavior.
- Capture stack traces for call-path evidence.
## Common Mistake
**Mistake:** fixing where the error appears because it is visible.
**Better:** trace backward and fix where incorrect state is first introduced.
## Pair with Layered Defenses
After fixing the source, apply layered validation from `defense-in-depth.md` so similar failures are blocked earlier in the future.