From Kai Personal Claude
Daily maintainer workflow -- triage, GitHub issue, worktree, fix, PR, review feedback loop. BUG/FEATURE auto-classify.
How this skill is triggered — by the user, by Claude, or both
Slash command
/kai:maintainerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- AUTO-GENERATED from SKILL.src.md -- do not edit directly. Run ./build.sh to rebuild. -->
SKILL.src.mdbuild.shreferences/cleanup-and-rules.mdreferences/force-ui-evidence.mdreferences/github-and-decompose.mdreferences/implement-and-pr.mdreferences/review-feedback-loop.mdreferences/scan-reviews.mdreferences/signal-intake.mdreferences/triage-and-classify.mdreferences/ui-diff-evidence.mdscripts/maintainer-status.cjsEnd-to-end issue triage and resolution pipeline for daily maintenance work.
Language rule: ALL output (conversation, reports, issues, PRs, commits) MUST be in English. If user input is in another language, translate and process in English. Never switch languages mid-conversation.
| Flag | Description |
|---|---|
| (input) | User report, transcript, image, GitHub issue URL, or plain description |
--tdd | Tests-first: write regression tests before code changes, verify after |
--parallel | Multi-agent parallel execution (plan with file ownership → cook with parallel agents) |
--deep | Deep analysis: per-area scouting + thorough research before implementation |
--force-ui | Hard-gate UI evidence: block PR until screenshots/report exist and are user-approved |
--skip-issue | Skip GitHub issue creation (issue already exists) |
--skip-worktree | Skip worktree creation (already in dedicated branch) |
--pr-main | Target PR to main instead of dev |
--decompose | Decompose into parallel sub-issues with separate worktrees (auto-suggested if >3 files across >2 domains) |
--scan-reviews | Standalone mode: scan open PRs for unresolved review feedback |
--from-signals | Observe-only intake: read drift/submodule/CI/issues/PRs via the suggester, rank, and suggest the next target. Read-only, no autonomy, no cron. |
--max-fix-attempts N | Max review feedback loop iterations (default: 5) |
--no-review-loop | Skip review feedback loop after PR creation |
"PR done" | Post-merge cleanup: terminate worktree(s) + branches |
Composable flags: --tdd, --parallel, --deep, --force-ui can combine (e.g., --parallel --tdd --force-ui).
Analyze input and classify as BUG or FEATURE:
| Type | Signals | Route |
|---|---|---|
| BUG | "broken", "error", "crash", "doesn't work", "regression", label:bug | Step 1a -> Step 2 -> Step 5 (/fix) |
| FEATURE | "add", "new", "would be nice", "enhance", "improve", label:feat/enhancement | Step 2.5 -> Step 3 -> Step 4 -> Step 5 (/cook) |
| Ambiguous | Default to BUG (safer, less overhead) | Step 1a -> Step 2 |
Track issue origin: Remember if GitHub issue was created by this workflow (Step 3) or provided by user input. Affects auto-sync in Step 5.5.
Flags propagate to underlying skills. Step 5 uses this matrix to select the right invocation:
| Classification | No flags | --tdd | --parallel | --deep | --parallel --deep | --parallel --tdd |
|---|---|---|---|---|---|---|
| BUG | /fix --parallel | /fix --parallel + TDD | /ck:plan --parallel → /cook --parallel | /ck:plan --deep → /cook --auto | /ck:plan --deep → /cook --parallel | /ck:plan --parallel --tdd → /cook --parallel --tdd |
| FEATURE | /cook --auto | /cook --auto --tdd | /ck:plan --parallel → /cook --parallel | /ck:plan --deep → /cook | /ck:plan --deep → /cook --parallel | /ck:plan --parallel --tdd → /cook --parallel --tdd |
Key rules:
--parallel or --deep always insert a /ck:plan step before implementation (even for BUGs)--deep controls planning thoroughness (per-area scouting); --parallel controls execution strategy (multi-agent). They're orthogonal — --parallel --deep means "plan deeply, execute in parallel"--tdd appends to whatever implementation skill is invoked--decompose is orthogonal: splits into sub-issues with separate worktrees (uses the above routing per sub-issue)implement-and-pr.md)Input (report/image/transcript/URL)
|
v
[0] --from-signals?
| |
YES NO
| |
v |
[0a] Signal |
intake: |
run |
suggester, |
rank |
candidates, |
USER picks |
target |
(read-only) |
| |
+------+-------+
|
v
[1] Auto-classify: BUG or FEATURE?
| |
BUG FEAT
| |
v | (skip brainstorm/debug)
[1a] /brainstorm |
| |
v |
[2] /debug |
| |
+--------+--------------+
|
v
[2.5] Complexity check (--decompose?)
| |
SIMPLE COMPLEX
| |
v v
[3] GitHub Issue [3+3.5] Parent + Sub-issues
(create or claim)
| |
v v
[4] /worktree Worktree per sub-issue
| |
v v
[4.5] Flag routing: --parallel/--deep → /ck:plan step
| |
v v
[5] Route by Flag Routing Matrix:
Default BUG: /fix --parallel
Default FEAT: /cook --auto
--parallel: /cook --parallel
--deep: /cook --auto
+--tdd: append --tdd
| |
+--------+-----------+
|
v
[5.5] GitHub progress comment
|
v
[6] /preview --explain (MANDATORY)
|
v
[7] /code-review codebase --parallel (optional)
|
v
[7.5] UI diff evidence + PR body (conditional)
|
v
[8] PR to dev --- Closes #N
|
v
[8.5] Review feedback loop (poll upstream + self /code-review -> /fix --parallel -> descriptive commits -> push -> repeat)
|
v
[9] "PR done" --- cleanup worktrees + branches
| Step | Name | Skip If |
|---|---|---|
| 0, 0a | Signal Intake (observe-only) | Not --from-signals |
| 1, 1a | Triage & Classify | -- |
| 2, 2.5 | Debug & Complexity | FEATURE path (skip 2, keep 2.5) |
| 3, 3.5 | GitHub Issue (Create or Claim) & Decompose | --skip-issue |
| 4 | Worktree | --skip-worktree or --decompose (3.5 handles) |
| 4.5 | Plan (flag-gated) | No --parallel/--deep flags |
| 5, 5.5 | Implement (flag-routed) & Update | --decompose (3.5 handles) |
| 6, 7, 8 | Review & PR | -- |
| 7.5 | UI Evidence & PR Composition | Non-UI change AND no --force-ui |
| 8.5 | Review Feedback Loop | --no-review-loop |
| 9 | Cleanup | -- |
| -- | Scan Reviews Mode | Not --scan-reviews |
All reports, brainstorm outputs, and plans MUST be saved in the TARGET REPO's plans/ directory, NOT in the worktree directory. Worktrees are for code changes only. Reports persist locally in the repo for backward debugging reference.
Observe-only intake activated by --from-signals. Surfaces what needs attention,
ranks it, and lets the USER pick the target. The chosen target becomes the input
to Step 1 (Auto-classify).
This mode is strictly read-only. No autonomy, no cron, no auto-pick, no ck-loop. It never branches, commits, pushes, edits files, or opens a worktree. Maintainer owns git ops -- this step only reads and suggests.
--from-signals is present, run Step 0a (signal intake) below, then feed
the user's chosen target into Step 1.--from-signals is absent, skip straight to Step 1 using whatever input the
user provided.Fallback when neither applies: if there is no --from-signals flag AND no
input (report / image / transcript / URL / description), ask the user what they
want to work on (or suggest re-running with --from-signals). Do not guess a
target, and do not run the suggester unless --from-signals was passed.
From the target repo root, run the bundled read-only suggester. It ships
with this skill at scripts/maintainer-status.cjs -- resolve the absolute
path from this skill's base directory (shown when the skill is invoked):
node "<skill-base-dir>/scripts/maintainer-status.cjs" --suggest --json
This helper only reads state (drift logs, submodule status, CI, open issues/PRs). It does not mutate anything.
Parse the JSON. Contract:
{
"timestamp": "...",
"root": "...",
"topTarget": <first candidate or null>,
"candidates": [
{
"type": "ci-failure|external-source-drift|submodule-dirt|open-issues|open-prs",
"priority": "CRITICAL|HIGH|MEDIUM|LOW",
"count": N,
"targets": [ ... ],
"guidance": "..."
}
]
}
Candidates arrive in priority order: CI > drift > submodule > issues > PRs.
topTarget is the first (highest-priority) candidate, or null when nothing
needs attention.
Present a ranked list to the user (topTarget first), one line per candidate showing type, priority, count, and guidance.
Ask the user which candidate to address. The user may instead override with a
fresh target -- a GitHub issue URL or a plain description. Never auto-pick,
even when there is only one candidate or a clear topTarget.
On pick, hand the chosen target to Step 1 (Auto-classify) as the input. From there, the normal BUG/FEATURE pipeline takes over.
Signal intake (read-only). Pick a target -- I will NOT choose for you.
1. [CRITICAL] ci-failure (1) -- main build red on <repo>; rerun or triage
2. [HIGH] external-source-drift (3) -- 3 vendored source pins behind upstream
3. [MEDIUM] submodule-dirt (1) -- <submodule> has uncommitted local work
4. [LOW] open-issues (4) -- 4 unassigned issues awaiting triage
5. [LOW] open-prs (2) -- 2 PRs awaiting review
Which one should I take? (number, a GitHub issue URL, or describe a target)
When the suggester reports no candidates (topTarget: null, empty candidates),
state that nothing needs attention and ask whether the user wants to provide a
target manually.
Steps 1, 1a, 2, 2.5 of the maintainer workflow.
If input is an image: Look at it directly (Claude is multimodal -- no need for external tools).
If input is a GitHub issue URL: Fetch via gh issue view <number> --json title,body,state,url,labels,assignees.
If input is a transcript/report: Parse and summarize.
Routing:
Skip if: FEATURE path.
Activate /brainstorm with the extracted context to:
/debug to validate before proceeding.Brainstorm report -> save to ROOT repo plans/ directory, not worktree.
If the issue is trivial (typo, one-liner), skip brainstorm and go to Step 2.
Then proceed to Step 2 (Debug).
Skip if: FEATURE path.
Activate /debug skill to:
Produce a concise diagnosis summary for the GitHub issue.
Evaluate whether this issue needs decomposition:
Auto-detection heuristic:
src/api/, src/db/, src/ui/)AskUserQuestionTrigger: Explicit --decompose flag OR auto-detection suggests it.
--decompose) -> Continue to Step 3 then Step 3.5 (decomposed flow)Steps 3, 3.5 of the maintainer workflow.
Before creating a worktree, ensure the issue is properly claimed upstream so others can see we're working on it.
# Parse issue number and repo from URL
ISSUE_NUM=$(echo "$ISSUE_URL" | grep -o '[0-9]*$')
REPO_NWO=$(echo "$ISSUE_URL" | sed 's|.*/\([^/]*/[^/]*\)/issues/.*|\1|')
# Fetch current issue state (already done in Step 1, reuse $ISSUE_JSON)
# Check assignment -- assign to @me if not already assigned
CURRENT_USER=$(gh api user --jq .login)
if ! echo "$ISSUE_JSON" | jq -e --arg u "$CURRENT_USER" '.assignees[] | select(.login == $u)' > /dev/null 2>&1; then
gh issue edit "$ISSUE_NUM" -R "$REPO_NWO" --add-assignee "@me"
fi
# Add classification label if missing (verify label exists first)
LABEL=$([ "$CLASSIFICATION" = "BUG" ] && echo "bug" || echo "enhancement")
REPO_LABELS=$(gh label list -R "$REPO_NWO" --json name --jq '.[].name')
if echo "$REPO_LABELS" | grep -qx "$LABEL"; then
if ! echo "$ISSUE_JSON" | jq -e ".labels[] | select(.name == \"$LABEL\")" > /dev/null 2>&1; then
gh issue edit "$ISSUE_NUM" -R "$REPO_NWO" --add-label "$LABEL"
fi
fi
After claiming, skip to Step 4 (worktree creation). Do NOT create a new issue.
Skip entirely if --skip-issue.
Skip if --skip-issue.
Determine the correct repo from the affected code paths. Use gh issue create:
Issue writing rules:
cd <REPO_ABSOLUTE_PATH> && gh issue create \
--title "<type>: <concise description>" \
--assignee "@me" \
--label "<appropriate-label>" \
--body-file - <<'EOF'
## Description
<brief problem summary>
## Root Cause
<concise findings from debug step>
## Affected Files
- <file1>
- <file2>
EOF
Use AskUserQuestion to confirm issue title and labels before creation.
Save the issue number for the worktree branch name.
Only if --decompose is active or auto-detection triggered -- fetch parent issue ID (needed for sub-issue linking in Step 3.5):
# Get parent issue ID (numeric ID, not issue number)
# REST API sub-issues endpoint needs this ID, not the issue number
PARENT_ID=$(gh api repos/${OWNER}/${REPO}/issues/${PARENT_NUM} --jq '.id')
# Also get node_id if GraphQL fallback needed
PARENT_NODE_ID=$(gh api repos/${OWNER}/${REPO}/issues/${PARENT_NUM} --jq '.node_id')
Skip if: Not triggered by --decompose flag or auto-detection.
1. Analyze findings into logical work streams:
2. Create sub-issues on GitHub and link to parent:
CRITICAL: Issue ID vs Issue Number GitHub's sub-issues API requires the actual numeric issue ID (e.g.,
4263129383), NOT the issue number (e.g.,992). Get the ID via:gh api repos/{owner}/{repo}/issues/{number} --jq '.id'
# Create sub-issue (no "Sub-task of" text -- GitHub UI shows relationship natively)
CHILD_URL=$(gh issue create -R {REPO} \
--title "{type}: {parent-title} -- {stream-description}" \
--assignee "@me" \
--label "{appropriate-label}" \
--body-file - <<'EOF'
## Scope
{files in this stream}
## Approach
{specific fix for this stream}
EOF
)
CHILD_NUM=$(echo "$CHILD_URL" | grep -o '[0-9]*$')
# Get actual issue ID (NOT issue number!) -- REST API requires numeric ID
CHILD_ID=$(gh api repos/${OWNER}/${REPO}/issues/${CHILD_NUM} --jq '.id')
# Link child to parent via REST API (preferred -- simpler than GraphQL)
LINK_RESULT=$(gh api repos/${OWNER}/${REPO}/issues/${PARENT_NUM}/sub_issues \
-X POST \
--input - <<< "{\"sub_issue_id\": $CHILD_ID}" 2>&1)
# Check for errors
if echo "$LINK_RESULT" | grep -q '"message"'; then
echo "[!] Sub-issue linking failed for #${CHILD_NUM}"
echo " Error: $(echo "$LINK_RESULT" | jq -r '.message // .')"
# Fallback: add body reference
gh issue edit "$CHILD_NUM" -R {REPO} \
--body "Part of #${PARENT_NUM}
$(original body)"
else
echo "[OK] Linked #${CHILD_NUM} as sub-issue of #${PARENT_NUM}"
fi
Alternative: GraphQL API (use if REST fails)
# Fetch node IDs (GraphQL requires node_id, not numeric id)
PARENT_NODE_ID=$(gh api repos/${OWNER}/${REPO}/issues/${PARENT_NUM} --jq '.node_id')
CHILD_NODE_ID=$(gh api repos/${OWNER}/${REPO}/issues/${CHILD_NUM} --jq '.node_id')
# Link via GraphQL mutation
gh api graphql -H GraphQL-Features:sub_issues \
-f parentId="$PARENT_NODE_ID" -f childId="$CHILD_NODE_ID" \
-f query='mutation($parentId: ID!, $childId: ID!) {
addSubIssue(input: { issueId: $parentId, subIssueId: $childId }) {
issue { title number }
subIssue { title number }
}
}'
3. Create worktree per sub-issue:
Activate /worktree for each sub-issue.
Each worktree branch: {prefix}/{type}/{parent-issue}-{stream-slug}
4. Launch parallel agents by classification (max 4):
Route per sub-issue using the Flag Routing Matrix. Each agent inherits active flags (--tdd, --parallel, --deep) from the parent invocation.
/fix --parallel via Task tool for each sub-issue in its worktree./fix --parallel with TDD substeps for each sub-issue./cook --auto via Task tool for each sub-issue. Include instruction: "Skip git commit at finalize."/cook --auto --tdd for each sub-issue./cook --parallel for each sub-issue (each agent internally uses multi-agent execution).Pass: sub-issue description, affected files, approach from decomposition, active flags.
5. Collect results: Wait for all agents to complete. Each agent commits to its own branch. Report summary of all streams.
After decomposition: Skip Steps 4-5 (already handled above). Continue to Step 5.5.
PR strategy for decomposed issues:
devWhen creating an epic (parent issue) with multiple child issues, use GitHub's sub-issues API.
| Term | Description |
|---|---|
| Issue Number | Human-readable number (e.g., #992) -- used in URLs and UI |
| Issue ID | Numeric database ID (e.g., 4263129383) -- required by sub-issues REST API |
| Node ID | GraphQL identifier (e.g., I_kwDOQNfRWc7-GjEn) -- required by GraphQL API |
OWNER=$(gh repo view --json owner --jq .owner.login)
REPO=$(gh repo view --json name --jq .name)
# 1. Create parent epic
EPIC_URL=$(gh issue create -R ${OWNER}/${REPO} \
--title "epic: Feature name" \
--assignee "@me" \
--label "enhancement" \
--body-file - <<'EOF'
## Overview
Brief description of the epic.
## Success Criteria
- [ ] Criterion 1
- [ ] Criterion 2
EOF
)
EPIC_NUM=$(echo "$EPIC_URL" | grep -o '[0-9]*$')
echo "Created epic #${EPIC_NUM}"
# 2. Create child issues (example: 3 children)
CHILDREN=(
"feat: Part 1 description"
"feat: Part 2 description"
"test: Part 3 tests"
)
for title in "${CHILDREN[@]}"; do
CHILD_URL=$(gh issue create -R ${OWNER}/${REPO} \
--title "$title" \
--assignee "@me" \
--body "Part of #${EPIC_NUM}")
CHILD_NUM=$(echo "$CHILD_URL" | grep -o '[0-9]*$')
# Get actual issue ID (NOT issue number!)
CHILD_ID=$(gh api repos/${OWNER}/${REPO}/issues/${CHILD_NUM} --jq '.id')
# Link as sub-issue
gh api repos/${OWNER}/${REPO}/issues/${EPIC_NUM}/sub_issues \
-X POST \
--input - <<< "{\"sub_issue_id\": $CHILD_ID}" \
--silent && echo "Linked #${CHILD_NUM} to epic #${EPIC_NUM}"
done
EPIC_NUM=991
CHILDREN=(992 993 994 995 998 999 1000 1001 1002)
for child_num in "${CHILDREN[@]}"; do
# Get actual issue ID (critical: NOT the issue number!)
child_id=$(gh api repos/${OWNER}/${REPO}/issues/${child_num} --jq '.id')
# Link via REST API
gh api repos/${OWNER}/${REPO}/issues/${EPIC_NUM}/sub_issues \
-X POST \
--input - <<< "{\"sub_issue_id\": $child_id}" \
--silent && echo "Added #${child_num} as sub-issue" || echo "Failed #${child_num}"
done
# List all sub-issues of an epic
gh api repos/${OWNER}/${REPO}/issues/${EPIC_NUM}/sub_issues \
--jq '.[] | "#\(.number): \(.title)"'
# Check sub-issue count and progress
gh api repos/${OWNER}/${REPO}/issues/${EPIC_NUM} \
--jq '.sub_issues_summary | "Total: \(.total), Completed: \(.completed), Progress: \(.percent_completed)%"'
| Error | Cause | Fix |
|---|---|---|
"sub_issue_id" is not of type integer | Passed issue number instead of ID | Use gh api .../issues/{num} --jq '.id' |
"sub_issue_id" is not of type integer (with string) | Used -f instead of --input | Use --input - <<< '{"sub_issue_id": 123}' |
Not Found (404) | Wrong repo or issue doesn't exist | Verify repo and issue number |
Resource not accessible | Missing permissions | Check gh auth status scope |
Steps 4, 4.5, 5, 5.5, 6, 7, 8 of the maintainer workflow.
Skip if: --decompose triggered (Step 3.5 handles worktrees) or --skip-worktree.
Worktree is REQUIRED for all maintainer work. NEVER make code changes directly on the original repo directory.
--skip-worktree is only allowed when already inside a dedicated worktree/branch.
Activate /worktree command with the issue context:
fix-duplicate-nav-links)fix-123-duplicate-nav-linksCRITICAL — Hotfix branch origin (--pr-main only):
When --pr-main is used, the worktree MUST be created from main, NOT from dev:
git worktree add ../worktrees/<name> -b <branch-prefix>/hotfix/<slug> origin/main
Branching from dev causes dev-only commits (dev release tags, chore: merge main into dev) to leak into the main branch merge, triggering unexpected production releases. This has caused phantom version bumps in the past (a hotfix branch from dev carried dev history into main).
After worktree creation, cd into the worktree directory for code work.
Run if: --parallel or --deep flag is set. Otherwise skip to Step 5.
When these flags are present, a planning step runs BEFORE implementation — even for BUGs. This ensures file ownership boundaries (--parallel) or per-area scouting (--deep) are established before agents start writing code.
| Maintainer Flag | Plan Invocation |
|---|---|
--parallel | /ck:plan --parallel <issue-context> |
--deep | /ck:plan --deep <issue-context> |
--parallel --deep | /ck:plan --deep <issue-context> (deep subsumes parallel's research) |
--parallel --tdd | /ck:plan --parallel --tdd <issue-context> |
--deep --tdd | /ck:plan --deep --tdd <issue-context> |
Plan context to provide:
codebase-summary.md, code-standards.md, etc.)Plan output: Plan directory with plan.md + phase-XX-*.md files. Save to ROOT repo plans/ dir (not worktree).
After plan: Proceed to Step 5 using plan path.
Skip if: --decompose triggered (Step 3.5 handles implementation).
Route based on classification + active flags. See "Flag Routing Matrix" above for the full matrix.
Activate /fix --parallel in the worktree with full context:
The fix skill handles: debug -> implement -> verify -> review -> finalize.
With --tdd: Append --tdd flag. Fix will write regression tests before applying changes, then verify tests pass after.
Activate /cook --auto in the worktree with full context:
With --tdd: Activate /cook --auto --tdd instead.
Cook handles the full cycle: research -> plan -> implement -> test -> review.
When --parallel flag is set, Step 4.5 already produced a plan with file ownership boundaries.
Activate /cook --parallel <plan-path>/plan.md in the worktree:
fullstack-developer agents per parallel phase groupWith --tdd: Activate /cook --parallel --tdd <plan-path>/plan.md.
When --deep flag is set, Step 4.5 already produced a thorough plan with per-area scouting.
Activate /cook --auto <plan-path>/plan.md in the worktree:
With --tdd: Activate /cook --auto --tdd <plan-path>/plan.md.
Cook MUST stop before its finalize step -- do NOT let cook commit or create PRs. Maintainer owns git operations (Steps 6-8).
To prevent cook from committing, include in the cook prompt:
"Skip the git commit step at finalize. Do not commit or push. Only update docs if warranted."
Skip if: No GitHub issue linked (e.g., --skip-issue was used and no URL provided).
Behavior by issue origin:
AskUserQuestion to confirm before posting.gh issue comment {ISSUE_NUMBER} -R {REPO} --body-file - <<'EOF'
## Progress Update
**Root Cause:** {1-2 sentence summary from debug step}
**Fix Applied:**
- {file1}: {what changed}
- {file2}: {what changed}
**Tests:** {added/modified/existing pass}
**PR:** #{PR_NUMBER} targeting `dev`
EOF
Rules:
After implementation, generate explanation of changes:
/preview --explain <summary-of-what-changed>
Then ask: "Any questions about the implementation before proceeding?"
Why: Prevents "AI Delegation" anti-pattern -- ensures user comprehends changes before ownership transfer.
Before any git operations, ask the user via AskUserQuestion:
"Run
/code-review codebase --parallelto validate code quality before committing?"
Skill tool with skill: "code-review" and args codebase --parallel -- address any findings before proceedingSkip if: the change has no user-facing UI impact AND --force-ui is NOT set.
Hard gate if --force-ui: PR creation (Step 8) is BLOCKED until evidence is produced and user-approved. No exceptions — even for backend-only changes, produce terminal evidence.
Auto-trigger (without --force-ui) when changed files touch user-facing surfaces such as ui/, web/, frontend/, dashboard/, routes, components, styles, templates, or onboarding/setup flows.
Select the right mode — NOT every change needs before/after. See references/force-ui-evidence.md for full mode selection logic.
| Mode | When | Output |
|---|---|---|
| Before/After | Bug fix, behavior change — meaningful visual difference exists | Side-by-side comparison |
| Targeted | New feature, new component — no meaningful "before" state | Annotated screenshot(s) with callout bounding boxes |
| Terminal | CLI output, test results, API responses | <pre> blocks with pass/fail highlighting |
| Hybrid | Mix of above | Combined sections in one report |
#ef4444 border only, no fill, no text, ~12px expanded padding2-4 meaningful capturesreferences/force-ui-evidence.md — store in .github/pr-assets/{PR}/<Link to> not <a href> (see references/ui-diff-evidence.md)references/force-ui-evidence.mdPR TARGET RULE (MANDATORY -- NO EXCEPTIONS):
dev -- ALL PRs from worktrees/feature branches MUST target dev--pr-main flag provided by usermain -- even if branch looks like a hotfix, target dev unless user said --pr-maindev before mainAfter fix is complete:
/kai:commit (analyzes changes, groups files, creates granular conventional commits)
--pr-main commit audit: Before committing, verify ALL commits use release-worthy types (hotfix:, fix:, feat:). Housekeeping commits (version sync, merge conflict resolution, package.json parity) MUST use chore: — never hotfix: or fix:. A chore: commit won't trigger a production release; a mistyped hotfix: will create an unexpected version bump.dev via /git pr to dev
--pr-main flag was explicitly provided: target main instead--pr-main one-shot rule: Each hotfix issue gets ONE PR to main. If the first hotfix PR is already merged and a follow-up fix is needed for the same issue, the follow-up MUST go through dev (normal flow), NOT as a second --pr-main PR. Multiple hotfix PRs to main for the same issue cause cascading version bumps.Closes #123)HEAD SHA
htmlpreview.github.io for the static HTML reportStep 7.5 of the maintainer workflow. Trigger logic lives in implement-and-pr.md Step 7.5 — this file covers HOW to capture, not WHEN.
Purpose: make user-facing PRs reviewable. UI evidence tells reviewers what changed, where to look, and whether the diff is trustworthy.
Capture actual product screenshots only:
Before/after captures MUST satisfy the parity checklist in force-ui-evidence.md.
Theme rule:
Target element MUST be centered in the viewport (~35-40% from top). Scroll the scrollable container so the review area sits mid-viewport with context above and below.
const rect = targetEl.getBoundingClientRect();
const main = document.querySelector('main');
main.scrollTop = rect.top + main.scrollTop - (window.innerHeight * 0.35);
NEVER use ImageMagick/convert — retina 2x scaling breaks coordinate math.
ALWAYS use DOM overlay injection. Full technique documented in force-ui-evidence.md (Callout Bounding Box Rules).
Scope: prefer 2-4 focused comparisons. Full-page captures only when layout/IA is the change.
NEVER push evidence commits incrementally. Capture locally, get user approval, THEN commit+push once.
1. Select evidence mode (see force-ui-evidence.md)
2. Capture screenshots (before/after or targeted)
3. Inject DOM overlays and capture final versions
4. Generate HTML report locally
5. Open report for user review → `open path/to/index.html`
6. User confirms evidence quality
7. THEN: commit all evidence + code in clean scoped commits
8. THEN: push once
9. THEN: update PR body with commit-pinned SHA URLs
Self-contained comparison report via /preview --html or hand-coded HTML. Report spec and templates in force-ui-evidence.md.
.github/pr-assets/{PR_NUMBER}/force-ui-evidence.md (Asset Storage & Naming)If no clean before baseline exists:
main, latest release, or stable fixturemain branch UIafter evidenceEvidence links MUST be commit-pinned (see force-ui-evidence.md Commit-Pinned URL Construction).
Do NOT:
Refresh when: later commits modify a captured surface, feedback says evidence is unclear, or callout targets wrong area.
When refreshed: replace locally → user approval → amend evidence commit → force-push once → update PR body.
When PR adds navigation links:
<Link to>, <NuxtLink>) — NOT <a href><a href> causes full reload, losing React state and storesExternalLink icon only for links leaving the SPAMask secrets, tokens, emails, account IDs, tenant names, and personal data before publishing. If sanitizing hides the UI change, recreate with safe fixture data.
Non-obvious pitfalls only (obvious rule inversions omitted):
--force-ui flag: hard-gate UI evidence workflow. Extracted from production-tested PR patterns.
| Scenario | Without --force-ui | With --force-ui |
|---|---|---|
| UI files changed | Step 7.5 auto-triggers (skippable) | Step 7.5 is BLOCKING — PR creation fails without evidence |
| Backend-only change | Step 7.5 skipped | Step 7.5 still runs — must produce terminal or API evidence |
| Evidence capture fails | Can proceed with warning | STOP and ask user — never open a weak PR |
When --force-ui is active, Step 8 (PR creation) is GATED on:
.github/pr-assets/{PR}/--force-ui is NOT just "before and after." Choose the right mode:
| Mode | When to Use | What to Produce |
|---|---|---|
| Before/After | Bug fix, behavior change, layout shift — meaningful visual difference exists | Side-by-side comparison with callout borders on BOTH states |
| Targeted | New feature, new component, first implementation — no meaningful "before" state | Single screenshot(s) with callout bounding boxes pointing to specific areas |
| Terminal | CLI output, test results, API responses, chat tool execution | <pre> blocks with pass/fail syntax highlighting |
| Hybrid | Mix of any above in the same PR | Combine sections in a single report |
Has meaningful "before" state?
├── YES → Before/After mode
│ (bug fix where old behavior is visible, layout change, config UI change)
└── NO
├── Is it a visual surface? → Targeted mode
│ (new feature, new page, new component, first-time dialog)
└── Is it output/CLI? → Terminal mode
(test output, API response, command result)
Targeted mode is the default for new features. Don't force a before/after when "before" is just an empty page — that adds noise, not signal.
The callout bounding box is the core technique. It answers: "where should the reviewer look?"
DOM overlay injection (NEVER ImageMagick — retina 2x scaling breaks coordinates):
const el = document.querySelector('{selector}');
const rect = el.getBoundingClientRect();
const pad = 12;
const overlay = document.createElement('div');
overlay.style.cssText = [
`position:fixed`,
`top:${rect.top - pad}px`, `left:${rect.left - pad}px`,
`width:${rect.width + pad * 2}px`, `height:${rect.height + pad * 2}px`,
`border:3px solid #ef4444`, `border-radius:10px`,
`pointer-events:none`, `z-index:99999`,
].join(';');
document.body.appendChild(overlay);
Style rules:
3px solid #ef4444 — no fill, no backgrounddocument.querySelectorAll('[style*="z-index:99999"]').forEach(e => e.remove())Multiple boxes — if PR touches 3 areas on one page, add 3 overlays. Use numbered labels if >2:
const label = document.createElement('div');
label.textContent = '1';
label.style.cssText = [
`position:fixed`,
`top:${rect.top - pad - 22}px`, `left:${rect.left - pad}px`,
`width:20px`, `height:20px`, `border-radius:50%`,
`background:#ef4444`, `color:#fff`, `font-size:12px`,
`display:flex`, `align-items:center`, `justify-content:center`,
`font-weight:700`, `pointer-events:none`, `z-index:100000`,
].join(';');
document.body.appendChild(label);
Before/after mode — every pair MUST match:
[ ] Same backend instance (or explicitly different: stable vs PR deployment)
[ ] Same user account and role
[ ] Same tenant/workspace (if multi-tenant)
[ ] Same theme (default: light for dashboards)
[ ] Same viewport size
[ ] Same route / page / dialog state
[ ] Fresh conversation/session (not reused state)
Targeted mode — document the capture environment:
[ ] Backend instance and version
[ ] User account and role
[ ] Theme (default: light)
[ ] What state/data is shown (seeded fixtures? real data? demo values?)
Document parity/environment in the HTML report AND the PR body.
Self-contained, no external deps, htmlpreview.github.io-friendly.
Use ONE palette across all report types:
Dark (default): --bg: #0d0d0d --fg: #e8e4df --card: #1a1714 --border: #2d2923 --muted: #8a837d --accent: #ef4444 --good: #22c55e --code: #151210
Light (.light): --bg: #f8f6f3 --fg: #1a1714 --card: #ffffff --border: #e5e0da --muted: #6b6560 --accent: #dc2626 --good: #15803d --code: #f3f0eb
| Element | Spec |
|---|---|
| Theme toggle | Top-right button, toggles .light class on <html>, text: ○ Light / ● Dark |
| Title bar | PR {N} · {Short Title} + environment parity statement |
| Summary cards | 3-column grid: What changed / Why it matters / Review cue |
| Comparison sections | 2-4 sections, each with heading card + content grid |
| Responsive | 2-col grid → 1-col at ≤900px via @media query |
Before/After: 2-column grid. Each column: .shot card with header (BEFORE/AFTER label + state description) + <img>.
Targeted: Single-column. .shot card with header (IMPLEMENTED label + state) + <img> of annotated screenshot.
Terminal: 2-column grid. Each column: .col-header (BEFORE/AFTER) + .panel containing <pre class="terminal"> with highlighted lines.
| Line Content | Class | Rendering |
|---|---|---|
| Failure, error, denied, blocked | .hl-bad | rgba(239,68,68,0.14) bg + 3px solid var(--accent) left border |
| Pass, success, allowed | .hl-good | rgba(34,197,94,0.14) bg + 3px solid var(--good) left border |
| Neutral | No class | Default monospace |
Wrap ONLY the meaningful lines — not entire blocks.
Use the template matching the evidence mode.
## Problem
{1-3 sentences}
Closes #{ISSUE}
## Visual Review
[Full comparison report]({htmlpreview-url})
### {N}. {Surface description}
| Before | After |
| --- | --- |
|  |  |
Before: {1 sentence}. After: {1 sentence}.
{Repeat for 2-4 comparisons}
All captures: {environment parity statement}. Red callout borders mark the review area.
## Root Cause
{Technical explanation}
## What changed
| File | Change |
|------|--------|
| `{path}` | {description} |
## Validation
- [x] {build passes}
- [x] {tests pass}
- [x] {evidence captured with matching state}
## Summary
{1-3 sentences describing what was implemented}
Closes #{ISSUE}
## Visual Review
[Full comparison report]({htmlpreview-url})
### {N}. {What was implemented}

Red callout boxes highlight: {what each box marks}.
{1-2 sentences on implemented behavior}.
{Repeat for 1-4 captures}
Captures: {environment statement}.
## What changed
| File | Change |
|------|--------|
| `{path}` | {description} |
## Validation
- [x] {build passes}
- [x] {tests pass}
- [x] {annotated evidence reviewed locally}
## Summary
- {bullet points}
Closes #{ISSUE}
## Root Cause
{Technical explanation}
## Changes
| File | Change |
|------|--------|
| `{path}` | {description} |
## Test plan
- [x] {build}
- [x] {tests}
- [ ] {manual verification}
Only commit final screenshots. No raw/unannotated backups.
.github/pr-assets/{PR_NUMBER}/
├── index.html # Self-contained comparison report
├── before-{surface-slug}.png # Before/After mode
├── after-{surface-slug}.png # Before/After mode
├── {surface-slug}.png # Targeted mode
├── before-{context}.txt # Terminal logs (optional)
└── after-{context}.txt # Terminal logs (optional)
SHA=$(git rev-parse HEAD)
# Image URL
https://raw.githubusercontent.com/{OWNER}/{REPO}/{SHA}/.github/pr-assets/{PR}/{filename}.png
# HTML report URL
https://htmlpreview.github.io/?https://raw.githubusercontent.com/{OWNER}/{REPO}/{SHA}/.github/pr-assets/{PR}/index.html
# Fork PRs — use fork owner
https://raw.githubusercontent.com/{FORK_OWNER}/{REPO}/{SHA}/.github/pr-assets/{PR}/{filename}.png
CRITICAL: Use commit SHA, not branch name. Branch-tip URLs go stale.
Step 8.5 of the maintainer workflow. Also used by --scan-reviews mode.
Skip if: --no-review-loop flag provided.
After PR is created (Step 8), automatically enter a fix loop that monitors review feedback (both AI bot AND upstream human reviewers) and resolves ALL issues until clean approval.
This step handles TWO review sources simultaneously:
/code-review codebase --parallel yourself each iteration to catch issues before reviewers doEvery loop iteration: poll upstream feedback AND run parallel self-review. Fix findings from BOTH sources in a single push.
| Setting | Default | Override |
|---|---|---|
| Max fix attempts | 5 | --max-fix-attempts N |
| Poll interval (first) | 150s | -- |
| Poll interval (subsequent) | 60s | -- |
| Max first review wait | 600s (10 min) | -- |
| Severity threshold | All (fix High, Medium, Low) | -- |
Parsed from AI review comment body (output of the repo's AI review workflow, if one is configured):
| Pattern | Severity | Action |
|---|---|---|
🔴 High | High | Fix (mandatory) |
🟡 Medium | Medium | Fix (mandatory) |
🟢 Low | Low | Fix (mandatory) |
| Pattern | Meaning | Loop Action |
|---|---|---|
✅ APPROVED | Clean approval | STOP -- only clean exit |
⚠️ APPROVED WITH NOTES | Has remaining issues | Continue fixing |
❌ CHANGES REQUESTED | Has blocking issues | Continue fixing |
✅ APPROVED or max attempts. No other exit condition.fix: guard null session in auth middleware not fix: address review feedback round 2.MAX_ATTEMPTS = --max-fix-attempts N or default (5)POLL_INTERVAL_FIRST = 150 seconds (initial wait for reviewer to process)POLL_INTERVAL = 60 seconds (subsequent polls)MAX_FIRST_WAIT = 600 seconds (10 min for first review to arrive)ATTEMPT = 0AI bot reviews:
gh api repos/{OWNER}/{REPO}/issues/{PR}/comments \
--jq '[.[] | select(.user.type == "Bot")] | sort_by(.created_at) | last'
Upstream reviews (PR reviews + inline comments):
gh api repos/{OWNER}/{REPO}/pulls/{PR}/reviews \
--jq '[.[] | select(.state == "CHANGES_REQUESTED" or .state == "COMMENTED")] | sort_by(.submitted_at) | last'
Also check inline review comments:
gh api repos/{OWNER}/{REPO}/pulls/{PR}/comments \
--jq 'sort_by(.created_at)'
POLL_INTERVAL_FIRST on first poll, POLL_INTERVAL on subsequent, retry until MAX_FIRST_WAIT exceededMAX_FIRST_WAIT exceeded with no review: alert user, proceed to Step 9Parallel self-review: While polling, also run /code-review codebase --parallel to proactively catch issues. Merge self-review findings with upstream feedback for a single fix pass.
gh api repos/{OWNER}/{REPO}/pulls/{PR}/commits --jq 'last | .commit.committer.date'
If review.created_at < latest_commit.date -> review is stale, skip and poll again.
AI bot reviews: Scan for 🔴 High, 🟡 Medium, 🟢 Low markers. ALL are actionable. Do NOT skip Low.
Check overall assessment per termination table above.
Upstream reviews: Parse inline comments, review body text, and requested changes. Treat ALL upstream feedback as actionable — if no severity markers present, treat every comment as at least Medium priority.
Self-review findings: Include findings from /code-review codebase --parallel run in this iteration.
ATTEMPTATTEMPT > MAX_ATTEMPTS:
/fix --parallel with PR number, review body, ALL severity items (High + Medium + Low), affected file hints/team cook instead/fix --parallel or /team./kai:commit and push
/kai:commit auto-analyzes changes, groups related files, and creates granular conventional commits with descriptive messages/kai:commit handles this correctlygit push
POLL_INTERVAL, goto step 1✅ APPROVED: proceed to Step 9Progress reporting per attempt:
[i] Review loop #{ATTEMPT}/{MAX_ATTEMPTS} -- {N_HIGH} high, {N_MEDIUM} medium, {N_LOW} low remaining -> delegating to [/fix --parallel | /team]...
The review bot posts via GitHub App. Filter comments by user type:
gh api repos/{OWNER}/{REPO}/issues/{PR}/comments \
--jq '[.[] | select(.user.type == "Bot")] | sort_by(.created_at) | last'
Compare review comment timestamp vs latest commit on PR branch:
# Latest commit timestamp
gh api repos/{OWNER}/{REPO}/pulls/{PR}/commits --jq 'last | .commit.committer.date'
# Review comment timestamp: from bot comment JSON .created_at
If review.created_at < latest_commit.date -> stale, skip and wait for fresh review.
Step 9 and operational rules for the maintainer workflow.
When user says "PR done" (meaning PR is merged):
BRANCH=$(git branch --show-current)
gh pr list --head "$BRANCH" --state open --json number,url
AskUserQuestion: "PR #N is still open (not merged). Merge it first before cleanup?"
Remove local worktree(s):
cd <REPO_ROOT> && git worktree remove <WORKTREE_PATH> --force
For decomposed issues: remove ALL sub-worktrees.
Delete local branch(es):
git branch -d <branch-prefix>/<type>/<slug>
Delete remote branch(es):
git push origin --delete <branch-prefix>/<type>/<slug>
Confirm cleanup complete.
gh unless the user explicitly asksCloses #N linkage is fine (auto-closes on merge) -- that's not a direct comment/kai:maintainer per issue in separate worktreesplans/ dir (not worktree)/fix regardless of original classification--no-review-loop skips Step 8.5 for quick PRs that don't need review monitoring--scan-reviews is standalone mode -- skips Steps 1-8, enters review loop directly for existing PRs✅ APPROVED terminates the review loop cleanly -- ⚠️ APPROVED WITH NOTES still triggers fixesStandalone mode activated by --scan-reviews -- skips Steps 1-8.
Scans open PRs across configured repos for unresolved review feedback.
Configure repos by providing OWNER/REPO and local absolute path for each repo to scan.
User specifies repos when invoking or stores in project configuration.
For each configured repo, fetch open PRs:
gh pr list -R {OWNER/REPO} --state open --json number,headRefName,url,title
For each open PR, fetch latest bot review comment:
gh api repos/{OWNER}/{REPO}/issues/{PR}/comments \
--jq '[.[] | select(.user.type == "Bot")] | sort_by(.created_at) | last'
Parse review body for severity markers (see review-feedback-loop.md)
Collect PRs with unresolved High/Medium/Low into fix queue
Report found PRs via AskUserQuestion -- confirm before proceeding
For each PR (sequential):
cd to repo absolute pathgit checkout the PR branch (exception to the "never modify original repo" rule -- the PR branch is already isolated)review-feedback-loop.md)Report summary of all processed PRs
Why sequential: Each /fix --parallel already spawns parallel subagents internally. Running multiple PR fixes concurrently would cause resource contention.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub kaitranntt/kai-personal-claude --plugin kai