posthog/issue-gate
Enforce GitHub issue preflight before starting implementation work. Use when beginning a new task to match it against open issues, claim an unstarted match, create a new issue when none matches, and abort only when matching is ambiguous or in-progress work belongs to another actor.
npx skills add https://github.com/PostHog/duckhog --skill issue-gate
Run issue preflight before any new implementation task. Keep GitHub Issues as the source of truth and return one decision: CREATE, CLAIM, ABORT_IN_PROGRESS, or ABORT_UNCERTAIN.
owner/repo)gh:gh api --paginate "/repos/owner/repo/issues?state=open&per_page=100" | jq -s '
add
| [ .[]
| select(.pull_request | not)
| {
number: .number,
title: .title,
body: (.body // ""),
url: .html_url,
labels: [ .labels[]?.name ],
assignees: [ .assignees[]?.login ]
}
]'
ISSUE_GATE_MATCH_JSON.{
"decision": "CREATE|CLAIM|ABORT_IN_PROGRESS|ABORT_UNCERTAIN",
"matched_issue_number": 123,
"reason": "string",
"human_action": "string"
}
CREATE: create issue and continue task against the created issue.CLAIM: ensure actor assignment and labels, then continue task against the claimed issue. If the issue is already assigned to ACTOR, skip re-assigning.ABORT_IN_PROGRESS: stop task and hand off only when the matched issue is in progress and not assigned to the current actor.ABORT_UNCERTAIN: stop task and request human triage.Closes #$ISSUE).Provide qualitative matching from the runtime LLM with ISSUE_GATE_MATCH_JSON:
{
"classification": "clear|ambiguous|none",
"matched_issue_number": 123,
"reason": "short explanation"
}
Rules:
ISSUE_GATE_MATCH_JSON is required.ISSUE_GATE_MATCH_JSON returns ABORT_UNCERTAIN.classification=clear requires a positive integer matched_issue_number.classification=ambiguous -> ABORT_UNCERTAIN.classification=none -> CREATE.classification=clear:ACTOR -> ABORT_IN_PROGRESS.ACTOR -> CLAIM.CLAIM.In-progress signal:
status:in-progress, orOwnership rule:
ACTOR, treat issue as owned by current actor and continue (CLAIM).ACTOR, abort (ABORT_IN_PROGRESS).gh api -X POST "/repos/$REPO/issues" -f "title=$TASK_TITLE" -F "body=@$TASK_BODY_FILE"gh api "/repos/$REPO/issues/$ISSUE" --jq '.assignees[]?.login' | grep -Fxq "$ACTOR" || gh api -X POST "/repos/$REPO/issues/$ISSUE/assignees" -f "assignees[]=$ACTOR"gh api -X POST "/repos/$REPO/issues/$ISSUE/labels" -f "labels[]=status:in-progress" || truegh api -X DELETE "/repos/$REPO/issues/$ISSUE/labels/status:todo" >/dev/null 2>&1 || truereferences/match-rubric.mdreferences/claude-usage.mdTake posthog/issue-gate from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.