| name | workflow-audit-emit-issues |
| description | After a codebase audit, groups audit findings by subsystem (path prefix) and emits them as context-rich GitHub issues with template discovery, label selection, and a batch preview-confirm gate before filing. Keywords: audit findings grouped github issues subsystem emit. |
workflow-audit-emit-issues
Files codebase audit findings as grouped GitHub issues — one issue per subsystem, sub-sectioned by domain.
When to invoke
After a codebase audit has produced a ranked findings table and the user accepts the
"File as issues?" offer emitted by the audit-codebase command. Requires ≥1 finding
in the orchestrator's context.
When NOT to invoke
- Zero findings — the audit ends at the rendered table; no offer is made.
- Without a preceding audit — this skill consumes in-context finding data only.
- To re-run the audit itself — see
workflow-codebase-audit (read-only; no back-edge here).
Composition
This skill is the filing counterpart to (not a caller of) workflow-codebase-audit.
Filing logic mirrors workflow-bug-triage Phase 4 precedent: --body-file, .cmd
sidecar, preview-gate-then-confirm. Template and label discovery follows the same
chain used by agents/product-manager.md.
Input
Findings already in the orchestrator's context from the audit render. Each finding
carries: file_line, domain, severity, confidence, effort, symptom,
root_cause, reasoning_chain, counter_evidence_considered, suggested_fix.
Preamble — bind plugin root
Resolve and bind the plugin root before any script call. If the runtime scripts directory is
missing (e.g. cwd is a worktree where CLAUDE_PLUGIN_ROOT is unset), abort loudly.
_RT="${CLAUDE_PLUGIN_ROOT:-$(git rev-parse --show-toplevel)}"
[ -f "$_RT/runtime/clean-state-files.sh" ] || {
echo "swe-workbench runtime scripts not found under $_RT/runtime — set CLAUDE_PLUGIN_ROOT and retry." >&2
exit 1
}
Phases
Phase 1 — Group by subsystem
For each finding derive a subsystem from file_line:
- Take the directory portion of the path (strip the filename).
- Use up to the first 2 path segments as the grouping key.
- Display label = last segment of that key:
src/auth/handler.ts → key src/auth → label auth
commands/audit-codebase.md → key commands → label commands
scripts/validate.py → key scripts → label scripts
- Root-level file (directory portion is empty, e.g.
README.md) → subsystem root.
- No path (tool-level or unknown finding) → subsystem
misc.
Within each subsystem, sub-group findings by domain (security, perf, reliability,
tooling, testing) to form sections within the issue body.
Report: "N findings across M subsystems."
Phase 2 — Discover templates + labels
Template discovery:
ls .github/ISSUE_TEMPLATE/*.md 2>/dev/null
Skip config.yml. Read frontmatter of the first .md match. If none, use the
default body schema below.
Label discovery:
gh label list --json name -q '.[].name'
If gh is unauthenticated or returns an error, treat the list as empty, warn in
the preview, and retain the .cmd sidecar so the user can run it manually.
Seed label per subsystem from the dominant domain of its findings:
| Domain | Preferred label (case-insensitive substring match) |
|---|
| security | "security" or "bug" |
| perf | "performance" |
| reliability | "bug" |
| tooling | "chore" or "tooling" |
| testing | "test" |
Dominant domain = the domain with the most findings in that subsystem.
Tie-break: prefer the higher-severity domain (security > reliability > perf > tooling > testing);
break further ties alphabetically.
Chain: template-frontmatter label → substring match → omit --label entirely.
Always surface the chosen label (or "none") in the preview so the user can change
it before replying confirm.
Phase 3 — Render bodies + batch preview
Derive <repo-slug> first:
gh repo view --json nameWithOwner -q '.nameWithOwner' | tr '/' '-'
Fallback (offline): basename $(git rev-parse --show-toplevel).
- Build one issue body per subsystem using the schema in Output below.
- Write each to a temp file via the Write tool:
/tmp/audit-emit-<repo-slug>-<unix-ts>-<n>.md
- Write a
.cmd sidecar holding all gh issue create lines (one per subsystem).
- Print the batch preview:
Target repo: <owner>/<repo>
[1] [audit] auth — label: security — 3 findings
[2] [audit] commands — label: bug — 1 finding
[3] [audit] misc — label: none — 2 findings
<fenced body for each group>
⚠ Re-running re-files duplicates. Confirm only once.
Reply `confirm` to file all · `drop N` to remove group N · `edit N` to revise group N
Phase 4 — File on confirm
| Reply | Action |
|---|
confirm (literal) | Run each sidecar gh issue create line; return issue URLs. After all issues are filed successfully, reap the temp files (foreground, no 2>/dev/null) then post-check each path (see below). |
drop N | Remove group N from the batch; rewrite temp files and sidecar; re-print preview. |
edit N | Show group N's body; accept revised text; rewrite temp file; re-print preview. |
| anything else | Re-prompt; do not file. |
After a successful confirm, reap all temp files and verify each was removed:
bash "$_RT/runtime/clean-state-files.sh" \
/tmp/audit-emit-<repo-slug>-<unix-ts>-1.md \
… \
/tmp/audit-emit-<repo-slug>-<unix-ts>-N.md \
/tmp/audit-emit-<repo-slug>-<unix-ts>.cmd
for f in /tmp/audit-emit-<repo-slug>-<unix-ts>-1.md … /tmp/audit-emit-<repo-slug>-<unix-ts>.cmd; do
[ -e "$f" ] && echo "⚠ state file NOT reaped: $f" >&2 || echo "✓ state file reaped: $f"
done
Use the actual paths written in Phase 3 (pattern: /tmp/audit-emit-<repo-slug>-<unix-ts>-<n>.md and .cmd).
On gh issue create failure: surface the error, print the URLs of successfully-filed
issues so far, and rewrite the .cmd sidecar to contain only the remaining (unfiled)
lines. Print the updated sidecar path for manual retry — do NOT surface the original
sidecar, as retrying it would duplicate already-filed issues.
Output: grouped issue body schema
## Audit findings — <subsystem>
> Scope: `<dominant-domain(s)>` · Severity range: `<highest>–<lowest>`
> Source: codebase audit
### <domain-section-1>
| Severity | File:Line | Symptom | Suggested fix |
|----------|-----------|---------|---------------|
| High | `path/to/file.ts:42` | Short symptom | Short fix |
**Root cause:** …
**Reasoning chain:** …
**Counter-evidence considered:** …
---
*(repeat for each domain section)*
## Provenance
Filed by `workflow-audit-emit-issues` from an in-session codebase audit.
Re-running re-files — check for duplicates before confirming.
Issue-filing command (per subsystem)
gh issue create \
--title "[audit] <subsystem>: <dominant-domain> findings" \
--body-file /tmp/audit-emit-<repo-slug>-<unix-ts>-<n>.md \
--label "<chosen-label>"
Omit --label when no matching label was found.
Absolute rules
- Never run
gh issue create before confirm. Render the preview and wait.
--body-file always. Never pass --body inline; never use the --template flag on gh issue create.
workflow-codebase-audit is read-only (see its invariant). This skill does not
re-invoke it — it only consumes findings already in context.
- Single batch gate. One preview → one
confirm → all issues filed.
- Zero findings → no offer. The calling command guards this; this skill assumes ≥1 finding.
- Subsystem = path prefix. Always derive from
file_line; never from domain alone.
Edge cases
| Scenario | Behaviour |
|---|
| Single-finding subsystem | Still its own issue |
| All findings have no path | One issue with subsystem misc |
| No label match | Omit --label; show "none" in preview |
No .github/ISSUE_TEMPLATE/ | Use default body schema |
gh unauthenticated | Warn in preview; surface .cmd sidecar path |
| Long body | --body-file handles it |
Common mistakes
| Mistake | Fix |
|---|
Running gh issue create before confirm | Wait for the literal reply |
Using --template flag | Use --body-file with the temp file path |
| Re-invoking the audit to refresh data | Consume in-context findings only |
| Omitting label feedback in preview | Always show "none" explicitly if no label matched |
| One giant issue for all findings | One issue per subsystem, sub-sectioned by domain |