Installs into .claude/skills of the current project.
Are you the author of Issue Refine?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/nightgauge-issue-refine)
---
name: issue-refine
description: Analyze a raw GitHub issue and rewrite it with structured sections,
acceptance criteria, and implementation guidance — making it immediately
pipeline-ready. Use before /nightgauge-issue-pickup on sparse or unstructured
issues.
license: Apache-2.0
metadata:
author: nightgauge
version: "1.0.3"
source: https://github.com/nightgauge/nightgauge
allowed-tools: Read Glob Grep Bash
disable-model-invocation: true
---
# Issue Refine
> Enrich a raw GitHub issue with structured sections and codebase context
## Description
This skill transforms a sparse or unstructured GitHub issue into a
pipeline-ready issue by:
1. Fetching the raw issue from GitHub
2. Detecting the issue type (bug or feature) from labels and body content
3. Performing codebase-aware analysis using `Glob`/`Grep` to find relevant files
4. Constructing a structured body with all required pipeline sections
5. Updating the issue on GitHub with the enriched content
6. Optionally calling `nightgauge issue mark-refined` (depends on #2533)
## Invocation
| Tool | Command |
| -------------- | ------------------------------------------- |
| Claude Code | `/nightgauge:issue-refine <N>` (via plugin) |
| OpenAI Codex | `$nightgauge-issue-refine <N>` |
| GitHub Copilot | Invoke via Agent Skills |
| Cursor | Invoke via Agent Skills |
## Arguments
```bash
# Refine a specific issue
/nightgauge:issue-refine 42
# Auto-detect from current branch
/nightgauge:issue-refine
```
The skill arguments (the text after the skill name) are the issue
number. If omitted, the issue number is inferred from the current branch.
## Philosophy
- **Preserve original content** — the original body is always saved in a
`<details>` block at the bottom
- **Codebase-grounded** — all file references come from actual `Glob`/`Grep`
results, never hallucinated
- **Non-destructive** — if required sections already exist, they are not
duplicated
- **Title-preserving** — the issue title is never modified
---
## Workflow
### Phase Marker Protocol
<!-- phase-registry: standalone-skill -->
This skill is standalone (not a pipeline execution stage), so its `stage="issue-refine"`
emits intentionally do not appear in `PHASE_REGISTRY`. The annotation above opts
the skill out of `scripts/validate-phase-markers.ts`.
At the start of each phase, emit a structured phase marker as an HTML comment on
its own line. Format:
`<!-- phase:start name="{phase-name}" index={N} total={T} stage="issue-refine" -->`
### Phase 0: Environment Preflight
### Phase 0: Environment Preflight
**PURPOSE**: Verify the nightgauge binary is available and the environment
is healthy before executing any pipeline stage. Skills halt immediately with
actionable instructions when this check fails.
Three-layer check:
1. **Binary discovery** — shell-level, runs before invoking the binary
2. **Doctor check** — binary-level, full environment health via `doctor --json`
3. **Project environment** — the checks the project declares under
`preflight.environment` (#101); opt-in, silent when none are declared
The binary discovery cascade below is **provider-neutral** (issue 4029): it works
identically under every adapter (Claude, Codex, Gemini, …) and references no
VSCode-extension-specific path. The host that spawns the skill (the VSCode
extension's `skillRunner`, or the Go `nightgauge` binary in auto/CLI mode)
resolves the binary authoritatively and exports `NIGHTGAUGE_BIN`, which
this cascade honors first. The remaining steps (PATH, the development-only repo
`bin/` and worktree canonical-repo `bin/`, `~/go/bin`) are fallbacks for direct
or terminal invocation.
This intentionally **diverges** from
`claude-plugins/nightgauge/hooks/lib/guard.sh` (issue 3262 → issue 4029): guard.sh is
Claude-Code-only and retains a trailing `~/.vscode/extensions/...` glob to serve
the standalone-terminal-Claude case (where no host exports `NIGHTGAUGE_BIN`).
Skills must stay portable Markdown and cannot reference that VSCode-only path —
so the shared resolution order (`NIGHTGAUGE_BIN` → PATH → gated repo → gated
canonical → `~/go/bin`) stays in sync, and only guard.sh carries the extra Claude-only
fallback. The `nightgauge preflight skill-portability` gate enforces that
no skill reintroduces a `.vscode/extensions` path, and that every cascade keeps
the `NIGHTGAUGE_DEV_REPO_BIN` gate (#2758): a foreign repository's
`bin/nightgauge` must never run.
After the cascade resolves `$BINARY`, the preflight prepends
`dirname($BINARY)` to `PATH` so subsequent bare `nightgauge ...`
invocations later in the same skill body resolve through PATH rather than
failing with "command not found" (issue 3262).
```bash
# Layer 1: provider-neutral binary discovery (NIGHTGAUGE_BIN → PATH → gated repo
# bin → gated canonical-repo bin → ~/go/bin). No VSCode-extension path — see issue 4029.
# Repo bins need NIGHTGAUGE_DEV_REPO_BIN=1 and a verified core checkout (#2758).
BINARY="${NIGHTGAUGE_BIN:-}"
[ -n "$BINARY" ] && [ ! -x "$BINARY" ] && BINARY=""
[ -z "$BINARY" ] && BINARY=$(command -v nightgauge 2>/dev/null || echo "")
if [ -z "$BINARY" ] && [ "${NIGHTGAUGE_DEV_REPO_BIN:-}" = "1" ]; then
for NG_ROOT in "$(git rev-parse --show-toplevel 2>/dev/null)" \
"$(cd "$(git rev-parse --git-common-dir 2>/dev/null)/.." 2>/dev/null && pwd)"; do
[ -x "$NG_ROOT/bin/nightgauge" ] || continue
grep -qx 'module github.com/nightgauge/nightgauge' "$NG_ROOT/go.mod" 2>/dev/null || continue
NG_ORIGIN="$(git -C "$NG_ROOT" config --get remote.origin.url 2>/dev/null)"
case "${NG_ORIGIN%.git}" in
git@github.com:nightgauge/nightgauge | https://github.com/nightgauge/nightgauge | \
ssh://git@github.com/nightgauge/nightgauge) BINARY="$NG_ROOT/bin/nightgauge"; break ;;
esac
done
fi
[ -z "$BINARY" ] && [ -x "$HOME/go/bin/nightgauge" ] && BINARY="$HOME/go/bin/nightgauge"
if [ -z "$BINARY" ]; then
echo "ERROR: nightgauge binary not found via NIGHTGAUGE_BIN, PATH or ~/go/bin" >&2
echo "" >&2
echo "Install via: go install github.com/nightgauge/nightgauge/cmd/nightgauge@latest" >&2
echo "Or download from: https://github.com/nightgauge/nightgauge/releases" >&2
echo "Run \`nightgauge doctor\` after installing to verify your environment." >&2
exit 1
fi
# Make the resolved binary callable via bare `nightgauge ...` later in this skill body (issue 3262).
export PATH="$(dirname "$BINARY"):$PATH"
# Per-repo GitHub token: export GH_TOKEN so every `gh` call in this skill
# authenticates as the *current repo's* configured user instead of the machine-
# global `gh auth` active account. `forge auth token` resolves per-repo (config
# github_user/token → GITHUB_TOKEN → `gh auth token --user <github_user>`), so
# concurrent sessions/workspaces owned by different GitHub users each use their
# own token. GH_TOKEN outranks GITHUB_TOKEN and the gh keyring. Only resolve when
# unset (an upstream env value — e.g. the VSCode extension's terminal env —
# wins); never fail preflight over it. Mirrors hooks/lib/guard.sh (sync, issue 3262).
if [ -z "${GH_TOKEN:-}" ]; then
IB_REPO_TOKEN="$("$BINARY" forge auth token 2>/dev/null || true)"
if [ -n "$IB_REPO_TOKEN" ]; then
export GH_TOKEN="$IB_REPO_TOKEN"
export GITHUB_TOKEN="${GITHUB_TOKEN:-$IB_REPO_TOKEN}"
fi
unset IB_REPO_TOKEN
fi
# Layer 2: full environment health check via doctor --json
DOCTOR_RESULT=$("$BINARY" doctor --json 2>/dev/null)
DOCTOR_EXIT=$?
if [ "$DOCTOR_EXIT" -eq 2 ]; then
echo "ERROR: Environment check failed — nightgauge doctor reports broken environment." >&2
FAILED_CHECKS=$(printf '%s\n' "$DOCTOR_RESULT" | jq -r '.failed_checks[]? // empty' 2>/dev/null)
if [ -n "$FAILED_CHECKS" ]; then
echo "Failing check(s): $(echo "$FAILED_CHECKS" | tr '\n' ',' | sed 's/,$//')" >&2
fi
printf '%s\n' "$DOCTOR_RESULT" | jq -r '.errors[]' >&2 2>/dev/null || true
INSTALL_MSG=$(printf '%s\n' "$DOCTOR_RESULT" | jq -r '.install_instructions // empty' 2>/dev/null)
[ -n "$INSTALL_MSG" ] && echo "$INSTALL_MSG" >&2
exit 1
fi
if [ "$DOCTOR_EXIT" -eq 1 ]; then
echo "WARNING: Environment has non-critical issues:" >&2
printf '%s\n' "$DOCTOR_RESULT" | jq -r '.warnings[]' >&2 2>/dev/null || true
# Continue — warnings do not block skill execution
fi
# Layer 3: project-declared environment checks (preflight.environment, #101).
# Opt-in: with none declared this prints nothing and continues. The verb
# writes one `[environment-halt]` line per failed check, with its remedy.
if ! "$BINARY" preflight environment >/dev/null; then
echo "[environment-halt] Project environment check failed — fix the environment, not the code." >&2
exit 1
fi
```
**Exit codes from `nightgauge doctor`**:
| Code | Meaning | Skill behavior |
| ---- | ------------------------------- | --------------------------------------- |
| 0 | Healthy | Continue |
| 1 | Degraded (warnings only) | Continue with warning printed to stderr |
| 2 | Broken (required checks failed) | Halt immediately with error details |
**Layer 3** halts the stage when any check declared under
`preflight.environment` in the workspace's `.nightgauge/config.yaml` fails
(exit 1) or the declaration is invalid (exit 2). The failure is in the
operator's environment, not the code: do not edit code to work around it, do
not run the remedy yourself, and end the stage. The pipeline records it as
`environment_halt` and holds the issue for an operator.
Do not theorize about the cause beyond what is printed here — the failing
check name (`failed_checks`) and its `.errors[]` message are the complete
diagnosis (#277).
---
### Phase 1: Validate Prerequisites and Resolve Issue Number
```bash
printf '<!-- phase:start name="validate-prerequisites" index=1 total=6 stage="issue-refine" -->\n'
```
#### Step 1.1: Verify GitHub Authentication
```bash
nightgauge forge auth status || { echo "ERROR: Forge auth not configured. Run: nightgauge forge auth login"; exit 1; }
```
#### Step 1.2: Resolve Issue Number
```bash
# Use argument if provided, otherwise infer from current branch
ISSUE_NUMBER="${ARGUMENTS:-$(git branch --show-current 2>/dev/null | grep -oE '[0-9]+' | head -1)}"
if [ -z "$ISSUE_NUMBER" ]; then
echo "ERROR: No issue number provided and could not infer from branch."
echo "Usage: /nightgauge:issue-refine <issue-number>"
echo "Example: /nightgauge:issue-refine 42"
exit 1
fi
echo "Refining issue #${ISSUE_NUMBER}..."
```
#### Step 1.3: Fetch Issue Details
```bash
REPO="${NIGHTGAUGE_REPO:-$(nightgauge git repo-slug)}"
: "${REPO:?set NIGHTGAUGE_REPO or run inside a clone with an origin remote}"
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
# --comments returns only comments by users with write access (or a trusted
# bot); the rest never reach this skill (#2130).
ISSUE_JSON=$(nightgauge forge issue view "$ISSUE_NUMBER" --repo "$REPO" \
--json --comments 2>/dev/null)
if [ -z "$ISSUE_JSON" ] || [ "$(printf '%s\n' "$ISSUE_JSON" | jq -r '.number // empty')" = "" ]; then
echo "ERROR: Issue #${ISSUE_NUMBER} not found or not accessible."
exit 1
fi
ISSUE_TITLE=$(printf '%s\n' "$ISSUE_JSON" | jq -r '.title')
ISSUE_BODY=$(printf '%s\n' "$ISSUE_JSON" | jq -r '.body // ""')
ISSUE_STATE=$(printf '%s\n' "$ISSUE_JSON" | jq -r '.state')
ISSUE_LABELS=$(printf '%s\n' "$ISSUE_JSON" | jq -r '[.labels[].name] | join(",")')
echo "Issue: #${ISSUE_NUMBER} — ${ISSUE_TITLE}"
echo "State: ${ISSUE_STATE}"
echo "Labels: ${ISSUE_LABELS:-none}"
```
---
### Phase 2: Detect Issue Type and Analyze Existing Content
```bash
printf '<!-- phase:start name="detect-issue-type" index=2 total=6 stage="issue-refine" -->\n'
```
#### Step 2.1: Determine Issue Type
```bash
# Prefer explicit type label
if printf '%s\n' "$ISSUE_LABELS" | grep -q "type:bug"; then
ISSUE_TYPE="bug"
elif printf '%s\n' "$ISSUE_LABELS" | grep -q "type:feature"; then
ISSUE_TYPE="feature"
elif printf '%s\n' "$ISSUE_LABELS" | grep -q "type:chore"; then
ISSUE_TYPE="chore"
elif printf '%s\n' "$ISSUE_LABELS" | grep -q "type:docs"; then
ISSUE_TYPE="docs"
else
# Infer from body keywords
BODY_LOWER=$(echo "$ISSUE_BODY" | tr '[:upper:]' '[:lower:]')
if printf '%s\n' "$BODY_LOWER" | grep -qE '\b(bug|error|exception|crash|broken|fail|wrong|incorrect|regression|stack trace)\b'; then
ISSUE_TYPE="bug"
elif printf '%s\n' "$BODY_LOWER" | grep -qE '\b(add|implement|create|build|feature|enhancement|support|allow)\b'; then
ISSUE_TYPE="feature"
else
ISSUE_TYPE="feature" # safe default
fi
echo "No type label found — inferred type: ${ISSUE_TYPE}"
fi
echo "Issue type: ${ISSUE_TYPE}"
```
#### Step 2.2: Detect Existing Structured Sections
```bash
# Check which pipeline sections already exist to avoid duplication
HAS_SUMMARY=false
HAS_USER_STORY=false
HAS_ACCEPTANCE_CRITERIA=false
HAS_TECHNICAL_NOTES=false
HAS_ROOT_CAUSE=false
HAS_COMPLEXITY=false
printf '%s\n' "$ISSUE_BODY" | grep -q "^## Summary" && HAS_SUMMARY=true
printf '%s\n' "$ISSUE_BODY" | grep -q "^## User Story" && HAS_USER_STORY=true
printf '%s\n' "$ISSUE_BODY" | grep -qE "^## Acceptance Criteria" && HAS_ACCEPTANCE_CRITERIA=true
printf '%s\n' "$ISSUE_BODY" | grep -q "^## Technical Notes" && HAS_TECHNICAL_NOTES=true
printf '%s\n' "$ISSUE_BODY" | grep -q "^## Root Cause" && HAS_ROOT_CAUSE=true
printf '%s\n' "$ISSUE_BODY" | grep -q "^## Complexity" && HAS_COMPLEXITY=true
echo "Existing sections — Summary:${HAS_SUMMARY} AC:${HAS_ACCEPTANCE_CRITERIA} Technical:${HAS_TECHNICAL_NOTES}"
```
---
### Phase 3: Codebase-Aware Analysis
```bash
printf '<!-- phase:start name="codebase-analysis" index=3 total=6 stage="issue-refine" -->\n'
```
**PURPOSE**: Ground the refined issue in actual codebase files. All file
references in the output MUST come from this phase — never from model
generation.
#### Step 3.1: Extract Keywords from Issue
Extract component names, class names, file paths, and error terms mentioned in
the issue title and body:
```bash
# Extract PascalCase component/class names (likely TypeScript/Go identifiers)
COMPONENT_KEYWORDS=$(echo "${ISSUE_TITLE} ${ISSUE_BODY}" | \
grep -oE '[A-Z][a-zA-Z]{3,}(Service|Manager|Controller|Handler|Provider|View|Panel|Tree|Dashboard|Orchestrator|Selector|Config|Schema|Parser|Client|Router)' | \
sort -u | head -10)
# Extract file paths or module names explicitly mentioned
EXPLICIT_PATHS=$(echo "${ISSUE_TITLE} ${ISSUE_BODY}" | \
grep -oE '[a-zA-Z0-9_/.-]+\.(ts|go|tsx|json|yaml|md)' | \
sort -u | head -10)
# For bugs: extract error message keywords
if [ "$ISSUE_TYPE" = "bug" ]; then
ERROR_KEYWORDS=$(echo "$ISSUE_BODY" | \
grep -oE '"[^"]{5,50}"|\`[^`]{5,50}\`|Error: [^\n.]{5,50}' | \
head -5)
fi
echo "Component keywords: ${COMPONENT_KEYWORDS:-none}"
echo "Explicit paths: ${EXPLICIT_PATHS:-none}"
```
#### Step 3.2: Search Codebase for Related Files
```bash
RELATED_FILES=""
# Use the Grep tool (or rg if available) to find files containing each keyword.
# When executing in a bash block, rg provides the same capability as the Grep tool.
for KW in $COMPONENT_KEYWORDS; do
MATCHES=$(rg "$KW" \
--glob "*.ts" --glob "*.tsx" --glob "*.go" \
-l 2>/dev/null | head -3)
if [ -z "$MATCHES" ]; then
# Fallback to grep if rg not available
MATCHES=$(grep -r "$KW" \
--include="*.ts" --include="*.tsx" --include="*.go" \
-l 2>/dev/null | head -3)
fi
if [ -n "$MATCHES" ]; then
RELATED_FILES="${RELATED_FILES}
${MATCHES}"
fi
done
# Verify explicitly mentioned file paths exist
for PATH_REF in $EXPLICIT_PATHS; do
if [ -f "$PATH_REF" ]; then
RELATED_FILES="${RELATED_FILES}
${PATH_REF}"
fi
done
# Deduplicate and limit
RELATED_FILES=$(echo "$RELATED_FILES" | sort -u | grep -v '^$' | head -10)
echo "Related files found:"
echo "${RELATED_FILES:- (none — keyword search returned no results)}"
```
#### Step 3.3: Read Key File Context (Bug Path Only)
For bug issues, read the first 1-2 most relevant related files to understand the
code path. This improves the quality of the Root Cause Analysis section.
```bash
if [ "$ISSUE_TYPE" = "bug" ] && [ -n "$RELATED_FILES" ]; then
FIRST_FILE=$(echo "$RELATED_FILES" | head -1)
if [ -f "$FIRST_FILE" ]; then
echo "=== Reading key file: $FIRST_FILE ==="
# Read first 50 lines for context without overwhelming the agent
head -50 "$FIRST_FILE"
echo "=== (truncated) ==="
fi
fi
```
---
### Phase 4: Construct Refined Issue Body
```bash
printf '<!-- phase:start name="construct-body" index=4 total=6 stage="issue-refine" -->\n'
```
**PURPOSE**: Build the structured body using analysis from phases 2 and 3.
Using the original issue content, inferred type, and codebase file list from
Phase 3, construct a new issue body with the following structure. Reuse existing
sections if they were already present (do not duplicate them).
#### Required Sections
Build the body with these sections in order:
**1. Summary** (always include — expand the title into a clear description):
```markdown
## Summary
{2-4 sentence description of the problem or feature. For bugs: what behavior is
observed vs. expected. For features: what capability is being added and why.
Expand on the original title — do not just repeat it verbatim.}
```
**2. User Story** (features only):
```markdown
## User Story
As a {user persona — developer, user, admin, etc.},
I want {specific action or capability},
So that {business or user value delivered}.
```
**3. Acceptance Criteria** (always include):
```markdown
## Acceptance Criteria
- [ ] {concrete, testable outcome 1}
- [ ] {concrete, testable outcome 2}
- [ ] {concrete, testable outcome 3}
```
Each criterion must be observable and testable. Avoid vague criteria like "works
correctly" — instead: "When X happens, Y is displayed" or "Calling Z returns
status 200 with field F present."
**4. Technical Notes** (always include):
```markdown
## Technical Notes
**Affected files:**
{For each file in RELATED_FILES from Phase 3.2:}
- `{file_path}` — {one line: why this file is relevant}
{If RELATED_FILES is empty:}
**Note:** No files were identified by automated codebase search. Implementation
agent should investigate relevant files during pickup.
**Implementation approach:**
{1-3 sentences of implementation guidance. For bugs: how to reproduce and where
to look. For features: what approach to take and what patterns to follow.}
```
**5. Root Cause Analysis** (bugs only):
```markdown
## Root Cause Analysis
**Observed behavior:** {what the user sees happening}
**Expected behavior:** {what should happen instead}
**Suspected location:** {file or module where the bug likely originates, from
Phase 3.3 analysis — or "Unknown, requires investigation" if no files found}
**Reproduction steps:**
1. {step 1}
2. {step 2}
(Extract from issue body if present; otherwise note "Not provided — add before
implementation")
```
**6. Complexity Estimate** (always include):
```markdown
## Complexity Estimate
Size: {XS|S|M|L|XL} — {1-sentence rationale based on number of affected files
and scope of change}
```
Size heuristics:
- **XS**: Single file, <20 lines changed
- **S**: 1-2 files, focused change
- **M**: 3-5 files, moderate scope
- **L**: 5-10 files or cross-cutting concern
- **XL**: 10+ files or architectural change
**7. Related Issues** (include only when dependencies are discovered):
```markdown
## Related Issues
- Depends on #{N} — {reason}
- Part of #{N} — {relationship}
```
**8. Original Issue** (always append — preservation block):
```markdown
<details>
<summary>Original Issue (before refinement)</summary>
{ISSUE_BODY verbatim — even if empty, include the block}
</details>
```
#### Body Construction Rule
- Sections that **already exist** in `ISSUE_BODY` (detected in Phase 2.2) are
NOT added again. Instead, the existing section content is preserved as-is.
- The `<details>` preservation block is always appended, even if the issue body
was already structured.
- Do NOT modify the issue title.
---
### Phase 5: Apply Updates to GitHub
```bash
printf '<!-- phase:start name="apply-updates" index=5 total=6 stage="issue-refine" -->\n'
```
#### Step 5.1: Write Body to Temp File
Write the constructed body to a temp file to avoid shell argument escaping issues
with `nightgauge forge issue edit`:
```bash
BODY_FILE=$(mktemp /tmp/issue-refine-body-XXXXXX.md)
# Agent writes NEW_BODY content to $BODY_FILE here
cat > "$BODY_FILE" << 'BODYEOF'
{constructed body from Phase 4}
BODYEOF
```
#### Step 5.2: Update Issue Body
```bash
REPO="${NIGHTGAUGE_REPO:-$(nightgauge git repo-slug)}"
: "${REPO:?set NIGHTGAUGE_REPO or run inside a clone with an origin remote}"
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
nightgauge issue edit "$ISSUE_NUMBER" --repo "$REPO" --body "$(cat "$BODY_FILE")"
if [ $? -ne 0 ]; then
echo "ERROR: Failed to update issue #${ISSUE_NUMBER} body."
rm -f "$BODY_FILE"
exit 1
fi
rm -f "$BODY_FILE"
echo "Issue #${ISSUE_NUMBER} body updated."
```
#### Step 5.3: Add Type Label if Missing
```bash
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
if ! printf '%s\n' "$ISSUE_LABELS" | grep -qE '(^|,)type:'; then
nightgauge forge graphql -f query="mutation{addLabelsToLabelable(input:{labelableId:\"$ISSUE_NUMBER\",labelIds:[\"type:${ISSUE_TYPE}\"]}){clientMutationId}}" 2>/dev/null || \
echo "WARNING: Could not add type:${ISSUE_TYPE} label (may not exist in repo)."
fi
```
#### Step 5.4: Mark as Refined (Graceful Fallback)
Call the Go binary's `mark-refined` command if available. This depends on
issue #2533. If the command does not exist, skip silently.
```bash
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
BINARY="${NIGHTGAUGE_BIN:-}"
[ -n "$BINARY" ] && [ ! -x "$BINARY" ] && BINARY=""
[ -z "$BINARY" ] && BINARY=$(command -v nightgauge 2>/dev/null || echo "")
if [ -z "$BINARY" ] && [ "${NIGHTGAUGE_DEV_REPO_BIN:-}" = "1" ]; then
for NG_ROOT in "$(git rev-parse --show-toplevel 2>/dev/null)" \
"$(cd "$(git rev-parse --git-common-dir 2>/dev/null)/.." 2>/dev/null && pwd)"; do
[ -x "$NG_ROOT/bin/nightgauge" ] || continue
grep -qx 'module github.com/nightgauge/nightgauge' "$NG_ROOT/go.mod" 2>/dev/null || continue
NG_ORIGIN="$(git -C "$NG_ROOT" config --get remote.origin.url 2>/dev/null)"
case "${NG_ORIGIN%.git}" in
git@github.com:nightgauge/nightgauge | https://github.com/nightgauge/nightgauge | \
ssh://git@github.com/nightgauge/nightgauge) BINARY="$NG_ROOT/bin/nightgauge"; break ;;
esac
done
fi
[ -z "$BINARY" ] && [ -x "$HOME/go/bin/nightgauge" ] && BINARY="$HOME/go/bin/nightgauge"
[ -n "$BINARY" ] && export PATH="$(dirname "$BINARY"):$PATH"
if [ -n "$BINARY" ]; then
"$BINARY" issue mark-refined "$ISSUE_NUMBER" 2>/dev/null || true
fi
```
#### Step 5.5: Report Success
```bash
REPO="${NIGHTGAUGE_REPO:-$(nightgauge git repo-slug)}"
: "${REPO:?set NIGHTGAUGE_REPO or run inside a clone with an origin remote}"
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
ISSUE_URL=$(nightgauge forge issue view "$ISSUE_NUMBER" --repo "$REPO" --json 2>/dev/null | jq -r '.url // empty')
[ -n "$ISSUE_URL" ] || ISSUE_URL="https://github.com/${REPO}/issues/${ISSUE_NUMBER}"
echo ""
echo "✓ Issue #${ISSUE_NUMBER} refined successfully"
echo " URL: ${ISSUE_URL}"
echo " Type: ${ISSUE_TYPE}"
echo " Files referenced: $(printf '%s\n' "${RELATED_FILES}" | grep -c . 2>/dev/null || echo 0)"
echo ""
echo "Next step: /nightgauge-issue-pickup ${ISSUE_NUMBER}"
```
---
### Phase 6: Self-Assessment Epilogue
```bash
printf '<!-- phase:start name="self-assessment" index=6 total=6 stage="issue-refine" -->\n'
```
### Self-Assessment Epilogue
**PURPOSE**: Evaluate whether this skill's instructions matched reality during
this execution. This phase is **non-blocking** — skip entirely if any main phase
failed. A perfectly working skill produces **no output** from this phase.
> See [docs/SKILL_SELF_ASSESSMENT.md](https://github.com/nightgauge/nightgauge/blob/main/docs/SKILL_SELF_ASSESSMENT.md) for
> the full strategy, synthesis algorithm, and integration architecture.
#### When to Skip
- Any prior phase exited with an error before completing
- The skill was cancelled or timed out
- Running in `--dry-run` mode
#### Step 1: Evaluate Execution Friction
Review the execution that just completed. Answer these questions honestly:
1. **Command failures**: Did any command, script, or binary call in the skill
instructions fail? (e.g., script not found, wrong flags, missing binary)
2. **Workarounds**: Did you have to deviate from the skill instructions to
accomplish the goal? (e.g., used `gh` directly because a referenced script
didn't exist, skipped a step that referenced a nonexistent file)
3. **Stale references**: Did any file path, function name, API endpoint, or tool
referenced in the instructions not exist in the current codebase?
4. **Unclear instructions**: Were any instructions ambiguous enough that you had
to guess at the intended behavior?
5. **Missing instructions**: Was there a significant step you had to figure out
on your own that should have been documented in this skill?
**If ALL answers are "no" — write nothing and complete the skill normally.** The
goal is silence when everything works.
#### Step 2: Write Assessment Record
Only if friction was detected in Step 1. Write a single JSON file,
`assessments/{STAGE_NAME}-${ISSUE_NUMBER}.json` in the clone's pipeline state
directory, through the binary (JSON on stdin; never a direct file write):
```bash
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
nightgauge layout write pipeline "assessments/{STAGE_NAME}-${ISSUE_NUMBER}.json" <<'JSON'
{ ... the record below ... }
JSON
```
The assessment record MUST follow this schema:
```json
{
"schema_version": "1",
"skill": "{STAGE_NAME}",
"skill_file": "skills/nightgauge-{STAGE_NAME}/SKILL.md",
"issue_number": 42,
"timestamp": "2026-03-10T14:30:00Z",
"friction": [
{
"type": "command_failure",
"severity": "high",
"description": "hooks/lib/add-to-project.sh not found — script was deleted",
"skill_line_hint": "claude-plugins/nightgauge/hooks/lib/add-to-project.sh <issue-number>",
"actual_resolution": "Used gh api graphql to add issue to project board directly",
"suggested_fix": "Replace with: nightgauge project add <issue-number>"
}
]
}
```
**Friction types**: `command_failure`, `workaround`, `stale_reference`,
`unclear_instruction`, `missing_instruction`
**Severity levels**:
- `high` — instruction is **broken**. Required manual workaround to complete.
- `medium` — instruction is **misleading**. Agent adapted without user help.
- `low` — instruction is **suboptimal**. No functional impact.
**Rules**:
- **One record per execution** — multiple friction items go in the `friction`
array, not separate files.
- **Be specific** — quote the exact instruction text that was wrong. Not "some
commands didn't work" but "Step 5.2 calls `hooks/lib/add-to-project.sh` but
this script was deleted in commit 65915701."
- **Suggest the fix** — every finding MUST include `suggested_fix` with the
concrete SKILL.md change needed. Not "update the docs" but "replace
`hooks/lib/add-to-project.sh <N>` with `nightgauge project add <N>`."
- **Don't invent friction** — only report issues you actually encountered during
this execution. Do not speculate about potential problems.
#### Step 3: Validate and Complete
```bash
# Validate JSON if written
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
ASSESSMENT_FILE="$(nightgauge layout path pipeline "assessments/{STAGE_NAME}-${ISSUE_NUMBER}.json")"
if [ -f "$ASSESSMENT_FILE" ]; then
python3 -m json.tool "$ASSESSMENT_FILE" > /dev/null 2>&1 || \
echo "WARNING: Assessment record is not valid JSON" >&2
fi
```
---
## Error Handling
| Condition | Action |
| ----------------------------------- | ------------------------------------------------------------ |
| Forge auth not configured | Exit 1 with `nightgauge forge auth login` remediation |
| Issue not found | Exit 1 with clear message |
| Issue already fully structured | Detect existing sections, append only missing ones |
| No related files found | Include note in Technical Notes; do not fabricate file paths |
| `nightgauge forge issue edit` fails | Exit 1 with error output |
| `mark-refined` binary missing | Skip silently (graceful fallback) — depends on #2533 |
## Completion Checklist
- [ ] Issue body updated with structured sections
- [ ] Original content preserved in `<details>` block
- [ ] Type label added if it was missing
- [ ] `mark-refined` called (or gracefully skipped if #2533 not merged)
- [ ] Issue title unchanged
- [ ] All file references come from actual `Glob`/`Grep` results
- [ ] Next step (`/nightgauge-issue-pickup`) displayed