ZenHub pipeline dynamic query guide
Workspace Info Query#
Pipeline ID, Repository ID, and Issue Type ID differ per workspace.
Dynamically query based on the X-zh-workspace configured in the project root's
.mcp.json:
// Pipeline ID + Repository ID
const workspace = await mcp__zenhub__getWorkspacePipelinesAndRepositories();
// workspace.pipelines โ [{ id, name }, ...]
// workspace.githubRepositories โ [{ id, name }, ...]
// Issue Type ID
const issueTypes = await mcp__zenhub__getIssueTypes({ repositoryId });
// issueTypes โ [{ id, name, level }, ...]
// (Initiative L1, Project L2, Epic L3, Feature/Bug/Task L4, Sub-task L5, ...)
Query Issue Type IDs via
mcp__zenhub__getIssueTypes().
Two States: Pipeline vs GitHub State โ ๏ธ#
ZenHub tracks two independent states for every issue. Confusing them is the #1 source of "issue stuck open" bugs:
| State | Owner | What it means |
|---|---|---|
| Pipeline | ZenHub | The board column (New Issues, In Progress, Review/QA, Done, โฆ). Moving columns is purely a board position. |
| GitHub state | GitHub | The issue's actual open / closed flag. |
Key consequences:
-
Donepipeline โ closed. Moving an issue to aDonecolumn does not close the GitHub issue โ it staysopen.Donemeans "ready to close", not "closed". -
Closedpipeline = GitHubclosed, 1:1. ZenHub does not treat "Closed" as an ordinary column; it is wired directly to the GitHubclosedstate. ZenHub's own API says to judge open/closed by state, not by pipeline. -
An issue becomes truly Closed in exactly one of three ways:
- Dragged into the Closed pipeline (โ GitHub closes it), or
- Closed directly on GitHub (โ ZenHub auto-moves it to Closed), or
- A linked PR merges with
Closes #Nin its body (โ GitHub auto-closes โ ZenHub syncs to Closed).
This repo's policy = (B) "merge = Close". AI agents run full-stack E2E tests + review before merge, so a merged PR is treated as Done and Closed. We therefore rely on mechanism #3 (
Closes #N) and never park issues in aDonecolumn. Because #3 can silently fail (missing keyword, ZenHub sync lag), every merge is followed by a closure verification + fallback (see below).
Pipeline Transitions by Workflow#
Issue Processing Workflow#
New Issues โ Product Backlog โ Sprint Backlog โ In Progress โ Review/QA โ [Merge = Close โ Closed]
Note:
Donepipeline is not used. On PR merge,Closes #auto-closes the GitHub issue, which syncs the ZenHub issue to the Closed pipeline. Always verify the Closed state after merge and fall back to an explicit close if the sync did not happen.
| Step | Trigger | Pipeline / State |
|---|---|---|
| Issue creation | Automatic | New Issues |
| Backlog registration | After triage | Product Backlog |
| Sprint assignment | Sprint planning | Sprint Backlog |
| Start work | On branch creation | In Progress |
| Request review | After PR creation | Review/QA |
| Complete (merge = Close) | PR merge โ GitHub auto Close โ ZenHub sync | Closed (verify + fallback). Done not used. |
Priority-based Initial Placement (issue creation flows)#
Issues created via /zenhub:epic / /product:epic skip New Issues triage โ
they are placed directly by priority tier (see rules/zenhub-conventions.md
โ Priority Review and Pipeline Sorting):
| Target | Initial Pipeline |
|---|---|
| Project / Epic | Product Backlog |
| Story P0 (sprint specified) | Sprint Backlog |
| Story P0/P1 | Product Backlog |
| Story P2 | Icebox |
| Sub-task | Follows parent Story |
moveIssueToPipelinehas no in-pipeline position param โ move in descending priority order (P0 โ P1 โ P2).
MCP Call Examples#
// Query workspace info (once at session start)
const workspace = await mcp__zenhub__getWorkspacePipelinesAndRepositories();
const inProgress = workspace.pipelines.find(p = > p.name === " In Progress " );
const reviewQA = workspace.pipelines.find(p = > p.name === " Review/QA " );
// On work start (move to In Progress)
mcp__zenhub__moveIssueToPipeline({
issueId: " {issue.graphqlId} " ,
pipelineId: inProgress.id
})
// After PR creation (move to Review/QA)
mcp__zenhub__moveIssueToPipeline({
issueId: " {issue.graphqlId} " ,
pipelineId: reviewQA.id
})
// After PR merge: do NOT move to Done. " merge = Close " .
// `Closes #N` should auto-close GitHub โ ZenHub syncs to Closed.
// โ ๏ธ Verify it actually happened, fall back to explicit close otherwise:
// 1. GitHub is the source of truth for open/closed โ verify it first.
const ghState = await Bash(`gh issue view ${issueNumber} --json state -q .state`);
if (ghState.trim() !== " CLOSED " ) {
// `Closes #N` did not fire (missing keyword, cross-repo link, etc.) โ close explicitly
await Bash(`gh issue close ${issueNumber} --reason completed`);
}
// 2. Confirm ZenHub synced the GitHub closed โ Closed pipeline.
// Closing on GitHub normally syncs automatically; if lagging, force the state.
const closed = await mcp__zenhub__searchClosedIssues({ query: `#${issueNumber}` });
if (!closed.find(i = > i.number === issueNumber)) {
await mcp__zenhub__updateIssue({ issueId: " {issue.graphqlId} " , state: " CLOSED " });
}
GitHub Issue Creation Example#
const workspace = await mcp__zenhub__getWorkspacePipelinesAndRepositories();
const repoId = workspace.githubRepositories.find(r = > /* select GitHub repo */).id;
const issueTypes = await mcp__zenhub__getIssueTypes({ repositoryId: repoId });
mcp__zenhub__createGitHubIssue({
repositoryId: repoId,
title: " feat(scope): Feature title " ,
body: " ## Summary\n...\n\n## Acceptance Criteria\n- [ ] ... " ,
issueTypeId: issueTypes.find(t = > t.name === " Feature " ).id,
labels: [ " feature " ],
})
Workspace Initialization#
To set up a new workspace with the standard pipeline structure, use the /zenhub:init-workspace
command:
/zenhub:init-workspace
Standard structure:
New Issues โ Icebox โ Product Backlog โ Sprint Backlog โ In Progress โ Review/QA
If a Done pipeline exists, internal issues are Closed and manual deletion is guided.
Referencing Skills/Agents#
Files that reference this registry:
| File | Purpose |
|---|---|
commands/dev/run.md | Full workflow |
commands/dev/batch.md | Sequential issue processing |
commands/dev/issue.md | Single issue processing |
commands/agents/dev/issue-state-agent.md | Issue state management |
commands/agents/issue-processor-agent.md | Issue processing agent |
commands/zenhub/init-workspace.md | Workspace pipeline initialization |
commands/zenhub/manage.md | PR linking + pipeline move |
commands/zenhub/epic.md | Epic/Story ์์ฑ + ์ฐ์ ์์ยทํ์๋ผ์ธ ๋ฐฐ์น |
commands/zenhub/changelog.md | ๋ซํ ์ด์ ์๋์ฐ ์ง๊ณ โ ๋ฆด๋ฆฌ์ค ๋ ธํธ |
commands/zenhub/sprint-rollover.md | ๋ฏธ์๋ฃ ์ด์ ๋ค์ ์คํ๋ฆฐํธ carryover |
commands/zenhub/triage.md | ์ธ๋ถ ํผ๋๋ฐฑ dedup โ New Issues ์ธ์ |
commands/zenhub/rerank.md | ์ ์ญ ๋ฐฑ๋ก๊ทธ ์ฐ์ ์์ ์ฌ์ ๋ ฌ |
commands/zenhub/team-align.md | ํ ๋ถํยท์ฐ์ ์์ ์ถฉ๋ ๋ฆฌํฌํธ |
Important Notes#
- No Hardcoded IDs: All IDs must be dynamically queried via MCP tools
-
.mcp.jsonBased: Per-project workspace is determined by theX-zh-workspaceheader in.mcp.json -
ID Query: All IDs obtained via
getWorkspacePipelinesAndRepositories()+getIssueTypes()combination