From bugflow
Start bug workflow when beginning work on a Bugzilla ticket. Scrapes Bugzilla, creates dossier, analyzes code, generates plan. Use with a bug ID to begin automated workflow.
npx claudepluginhub konstantilieris/bugfow_pluginThis skill uses the workspace's default tool permissions.
- [Command](#command)
Orchestrates bug fixes using Task tools in a pipeline: dual RCA (Sonnet+Opus), consolidation, Codex validation, implementation, code review. Initializes via bun script.
Current session: !`source "${CLAUDE_PLUGIN_ROOT}/scripts/state.sh" && get_current_session 2>/dev/null || echo "No active session"`
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Start the complete bug/feature workflow pipeline - from Bugzilla scraping to validated plan.
/bug-start <bug_id> - Start workflow for a Bugzilla bug or feature request
Accepts:
6231 - Just the bug numberbug/6231 - Branch formatbugzilla.baseUrl - e.g. <bugzilla.baseUrl>/show_bug.cgi?id=6231bug-6231 - Hyphenated formatConfiguration Required: This skill requires project configuration. If .claude/bugflow/config/environment.json does not exist, prompt the user to run /bugflow:bug-setup first.
bugzilla.baseUrlThen user runs /bug-accept <bugId> to execute.
See INGEST.md for detailed ingest steps.
When spawning the bugzilla-scout agent, you MUST use this exact pattern:
Task({
description: "Scrape Bugzilla bug data",
prompt: "Read and execute ${CLAUDE_PLUGIN_ROOT}/infrastructure/agents/bugzilla-scout.md to scrape bug <bugId> from <bugzilla.baseUrl>/show_bug.cgi?id=<bugId>",
subagent_type: "general-purpose",
model: "opus"
})
WARNING: Do NOT use subagent_type: "bugzilla-scout". While this gives
Playwright tool access, it does NOT load the agent instructions from the .md file.
Without instructions, the agent won't know to call browser_navigate or
browser_snapshot, resulting in hallucinated data with 0 tool calls.
Why general-purpose? It has access to the Read tool, so the agent can read
its own instruction file before executing.
After receiving results from the scout agent, you MUST verify the data before proceeding:
Before parsing any JSON, check the raw scout output for evidence of real tool execution:
[RAW SNAPSHOT EXCERPT with actual page contentBug {bugId} or id={bugId}<function_results> blocks, not just <function_calls>If ANY of these fail → STOP immediately:
Error: Scout hallucinated / no tool evidence
The scout agent did not provide evidence of real MCP tool execution.
- Missing raw snapshot excerpt, OR
- Bug ID not found in excerpt, OR
- No function_results blocks (only simulated calls)
This bug's data cannot be trusted. Please:
1. Run /bug-reset {bugId}
2. Try /bug-start {bugId} again
3. If it fails again, manually verify at: <bugzilla.baseUrl>/show_bug.cgi?id={bugId}
DO NOT proceed to JSON parsing if the gate fails.
scoutResult.id === requestedBugIdscoutResult.title is not null/empty and has at least 3 charactersscoutResult.success === trueIf ANY check fails:
Log error with details:
Scout verification failed for bug {bugId}:
- Requested: {requestedBugId}
- Received: {scoutResult.id || 'null'}
- Title: {scoutResult.title || 'empty'}
- Error: {scoutResult.error || 'none'}
DO NOT proceed with workflow - do not create dossier or continue
Report to user:
Error: Scout returned invalid data for bug {bugId}
The scout agent may have hallucinated data. Please verify:
1. Bug exists at <bugzilla.baseUrl>/show_bug.cgi?id={bugId}
2. Try running /bug-reset {bugId} and /bug-start {bugId} again
Details: {verification failure reason}
If first attempt, retry ONCE with fresh browser state (the scout agent's Step 0.5 handles this)
// Pseudo-code for verification logic
function verifyScoutResult(scoutResult, requestedBugId) {
if (!scoutResult.success) {
throw new Error(`ScoutFailed: ${scoutResult.error || 'Unknown error'}`);
}
if (scoutResult.id !== requestedBugId) {
throw new Error(`BugIdMismatch: Requested ${requestedBugId} but got ${scoutResult.id}`);
}
if (!scoutResult.title || scoutResult.title.length < 3) {
throw new Error(`InvalidTitle: Scout returned empty or invalid title`);
}
return true; // Verification passed
}
| Agent | Purpose |
|---|---|
| bugzilla-scout.md (via general-purpose) | Scrape bug data from Bugzilla |
| bug-dossier-writer | Create dossier and state file |
| analysis | Determine FE/BE/MIXED/FEATURE verdict |
| fe-implementer-planner | Generate minimal-risk plan (FE/MIXED) |
| feature-designer | Design new feature (FEATURE) |
| test-spec-generator | Generate test specification |
| plan-validator | Validate plan quality |
| backend-doc-writer | Create BE handoff doc (BE/MIXED) |
| slack-notifier | Send WORKFLOW_STARTED notification |
INIT → SCOUT → DOSSIER → ANALYZE → VERDICT
↓
┌─────────┼─────────────┐
↓ ↓ ↓
FE/MIXED BE FEATURE
↓ ↓ ↓
PLAN_FE DONE_BE FEATURE_DESIGN
↓ ↓
└──────→ TEST_SPEC ←───┘
↓
VALIDATE
↓
(user approval via /bug-accept)
↓
EXECUTE → VERIFY → CLOSED
CRITICAL: TEST_SPEC state is MANDATORY for all FE, MIXED, and FEATURE verdicts.
The workflow MUST NOT proceed to VALIDATE without a valid testSpec in the state file.
| Output | Path |
|---|---|
| Dossier | <paths.dossierDir> from project config (default: docs/bugflow)/bug-<bugId>.md |
| State | <paths.stateDir> from project config (default: .bugflow)/<bugId>.json |
| Backend Doc | <paths.dossierDir>/bug-<bugId>-backend-doc.md (BE/MIXED only) |
| Logs | <paths.stateDir>/_logs/<bugId>.log.json |
Error: Cannot access Bugzilla at <bugzilla.baseUrl>
Possible causes:
- VPN not connected
- Bugzilla server down
- Network issue
Please check connectivity and try again.
Error: Bug <bugId> not found in Bugzilla
Verify the bug ID is correct: <bugzilla.baseUrl>/show_bug.cgi?id=<bugId>
Workflow already exists for bug <bugId>
Current state: <state>
Branch: <branch>
Use `/bug-status <bugId>` to check progress.
Use `/bug-reset <bugId>` to start over.
After planning completes (PLAN_FE or FEATURE_DESIGN), ALWAYS spawn test-spec-generator before proceeding to VALIDATE.
Task({
description: "Generate test specification",
prompt: "Read and execute ${CLAUDE_PLUGIN_ROOT}/infrastructure/agents/test-spec-generator.md for bug <bugId>.
State file: <paths.stateDir>/<bugId>.json
Use planResult from state file to create verification assertions.",
subagent_type: "general-purpose",
model: "sonnet"
})
Task({
description: "Generate test specification",
prompt: "Read and execute ${CLAUDE_PLUGIN_ROOT}/infrastructure/agents/test-spec-generator.md for bug <bugId>.
State file: <paths.stateDir>/<bugId>.json
Use featureDesignResult from state file.
Convert featureSpec.testCases to structured assertions.",
subagent_type: "general-purpose",
model: "sonnet"
})
testSpec field with non-empty assertions arrayTEST_SPEC state// Verification after test-spec-generator completes
const state = await readState(bugId);
if (!state.testSpec || !state.testSpec.assertions?.length) {
// HALT - Cannot proceed without testSpec
return {
error: "Test specification generation failed",
action: "RETRY_TEST_SPEC",
message: "testSpec is required for verification. Run test-spec-generator again."
};
}
// Update state to TEST_SPEC
await updateState(bugId, { state: 'TEST_SPEC' });
CRITICAL: Do NOT proceed to VALIDATE without a valid testSpec. This ensures the /bug-accept phase can run automated verification.
After plan validation passes, ALWAYS send Slack notification with full plan details before stopping for approval.
Read the state file and extract plan details based on verdict:
// Read state file
const state = await readState(bugId);
// Build planDetails based on verdict
let planDetails;
if (state.analysisResult.verdict === 'FEATURE') {
// FEATURE verdict - use featureDesignResult
const fdr = state.featureDesignResult || {};
planDetails = {
strategy: 'feature_implementation',
riskLevel: fdr.riskLevel || 'medium',
touchpoints: (fdr.touchpoints || []).slice(0, 10),
changes: (fdr.components || []).map(c => `Implement ${c.name}: ${c.description || ''}`.substring(0, 100))
};
} else {
// FE/MIXED verdict - use planResult
const pr = state.planResult || {};
const touchpoints = (pr.touchpoints || [])
.slice(0, 10)
.map(t => `${t.file}:${t.startLine || '?'}-${t.endLine || '?'}`);
const changes = [
...(pr.modifiedFunctions || []).map(f => `Modify ${f.name}(): ${f.description || ''}`),
...(pr.newFunctions || []).map(f => `Add ${f.name}(): ${f.description || ''}`)
].map(c => c.substring(0, 100)); // Truncate long descriptions
planDetails = {
strategy: pr.strategy || 'minimal_fix',
riskLevel: pr.riskLevel || 'low',
touchpoints: touchpoints,
changes: changes
};
}
function formatTouchpointsList(touchpoints, totalCount) {
if (!touchpoints.length) return '• (none)';
let lines = touchpoints.map(t => `• \`${t}\``);
if (totalCount > touchpoints.length) {
lines.push(`• _...and ${totalCount - touchpoints.length} more files_`);
}
return lines.join('\n');
}
function formatChangesList(changes) {
if (!changes.length) return '• (none)';
return changes.map(c => `• ${c}`).join('\n');
}
mcp__slack__slack_post_message({
channel_id: "read from project config `slack.channelId`",
text: `:bug: *Bug ${bugId} Workflow Started*
*Title:* ${title}
*Branch:* \`${branch}\`
*Verdict:* *${verdict}* (${confidence} confidence)
:clipboard: *Implementation Plan*
*Strategy:* ${planDetails.strategy}
*Risk Level:* ${planDetails.riskLevel}
*Files to Modify:*
${formatTouchpointsList(planDetails.touchpoints, totalTouchpointCount)}
*Changes:*
${formatChangesList(planDetails.changes)}
:page_facing_up: Dossier: \`<paths.dossierDir>/bug-${bugId}.md\`
:arrow_right: Run \`/bug-accept ${bugId}\` to execute`
})
For BE (Backend) verdict, the plan details are NOT included since no FE implementation is planned:
mcp__slack__slack_post_message({
channel_id: "read from project config `slack.channelId`",
text: `:wrench: *Bug ${bugId}: Backend Issue Detected*
*Verdict:* *BE* (Backend)
*Title:* ${title}
Backend analysis doc ready:
\`<paths.dossierDir>/bug-${bugId}-backend-doc.md\`
Please review and forward to backend team.`
})
If plan details are not available (planResult/featureDesignResult missing), fall back to minimal message:
if (!planDetails || !planDetails.touchpoints?.length) {
// Fallback - send minimal notification
text = `:bug: *Bug ${bugId} Workflow Started*
*Title:* ${title}
*Branch:* \`${branch}\`
*Verdict:* *${verdict}*
:page_facing_up: Dossier: \`<paths.dossierDir>/bug-${bugId}.md\`
:arrow_right: Run \`/bug-accept ${bugId}\` to execute`;
}
CRITICAL: This notification must be sent before displaying the plan to the user. Failures are logged but don't block workflow.
After validation passes and Slack notification is sent, you MUST display this exact output format:
═══════════════════════════════════════════════════════════════════════
WORKFLOW PAUSED — AWAITING USER APPROVAL
═══════════════════════════════════════════════════════════════════════
Bug {bugId}: {title}
Branch: {branch}
Verdict: {verdict}
Plan ready for review in: <paths.dossierDir>/bug-{bugId}.md
DO NOT IMPLEMENT — Run /bug-accept {bugId} to execute
Other commands:
/bug-plan {bugId} - Regenerate the plan
/bug-status {bugId} - Check workflow state
/bug-reset {bugId} - Start over
═══════════════════════════════════════════════════════════════════════
CRITICAL RULES:
DO NOT display the full plan inline — Only show the file path. Displaying the plan text inline causes Claude to treat it as an instruction to execute.
The stop banner is mandatory — It serves as a visual "stop sign" that prevents premature implementation.
Never say "Implement the following plan" — This phrase triggers implementation. Instead, say "Plan ready for review".
Why this works: By NOT displaying the full plan inline, and instead pointing to the dossier file, we avoid Claude (or a resumed session) treating the plan as an actionable instruction.