GitHub Projects Workflow¶
The XO Data Ops GitHub Project is the data team's single source of truth for work in progress. Every task — no matter how small — starts as an issue in the xo-data repo and flows through a defined lifecycle.
Purpose¶
This guide covers the day-to-day workflow: how issues move from idea to done, how sprints are structured, how branches and PRs connect to issues, and how Claude assists at each stage.
Prerequisites¶
Before using this workflow, ensure the following are in place. The setup checklist (.claude/ongoing/projects/github-projects-setup-checklist.md) covers execution details.
- XO Data Ops GitHub Project exists at the org level (xtendops-developers)
- Labels are synced to the canonical taxonomy (4 area labels:
pipelines,snowflake,xoos,platform) - Milestones are created for phased initiatives
- Custom project fields are configured (Priority, Domain, Effort, Iteration)
ghCLI is installed and authenticated for developers using Claude Code
Issue Lifecycle¶
Every issue moves through five stages. The Sprint Board view maps directly to these columns.
Backlog — The issue exists. It has a title and a native Issue Type. Everything else can be empty. This is where ideas land before they're scoped.
Refined — The issue is ready to be picked up. It has acceptance criteria, an area label, Priority and Effort project fields set, and a native Issue Type. A developer can read this issue and start working without asking follow-up questions.
In Progress — A developer has picked up the issue, assigned themselves, and created a branch. Work is underway.
In Review — A PR is open referencing the issue (Closes #N). A reviewer is assigned.
Done — The PR is merged. GitHub automation moves issues to Done when their linked PR merges. Issues should only be closed manually when no work was done: rejected before starting, cancelled, or duplicate (add a comment explaining why before closing).
What makes an issue "Refined"?¶
An issue is refined when all of these are true:
- Clear title with an imperative verb ("Add...", "Fix...", "Investigate...")
- Native Issue Type set (Feature / Bug / Task / Chore / Docs / Epic)
- Area label set (
pipelines,snowflake,xoos, orplatform) - Priority field set in the project board (P1 - Critical / P2 - High / P3 - Normal / P4 - Low)
- Effort estimate set via the project's Effort field (XS through XL)
- Acceptance criteria written as a checklist in the issue body
Issue Types¶
Issue types are set via GitHub's native Issue Types (not labels). Every issue must have exactly one type.
Feature — New capability. Use the feature issue template. Includes Goal, Acceptance Criteria, Background, and Out of Scope sections.
Bug — Something is broken. Use the bug template. Includes What's Broken, Expected vs. Actual Behavior, Steps to Reproduce, Impact, and Environment.
Task — Implementation work or time-boxed research. When used for research (spike), max 2 days, must produce a written artifact committed to the repo — an ADR, findings doc, or POC branch. A spike closes via a PR, even if the conclusion is "don't do this." Use the spike template for research tasks. For generic implementation work, use the feature template body (Goal + Acceptance Criteria).
Chore — Maintenance, cleanup, refactoring, dependency updates. Doesn't change user-facing behavior.
Docs — Documentation only. ADRs, guides, README updates, inventory maintenance.
Epic — Multi-sprint initiative grouping child issues. Uses [Epic] prefix in title.
Epics¶
An epic is a tracking issue that groups related issues under a larger initiative. GitHub doesn't have a native epic type, so we use a convention:
- Title prefix:
[Epic]— e.g.,[Epic] Product App Analytics Integration - Body contains a task list linking child issues:
- [ ] #12 Title,- [ ] #13 Title - Native Type: Epic
- Epics carry no area label — their children carry the area context.
- Milestone: the phase milestone it targets
- The epic stays open until all child issues are closed
- Always tied to a Milestone when representing external commitments.
Create an epic when an initiative spans more than 3 issues or more than 1 sprint. Break child issues down to M effort or smaller — if an issue is L or XL, it probably needs to be split further.
Branch Naming¶
Every branch follows this pattern:
The type matches the issue type: feature/, bug/, spike/, chore/, or docs/. The issue number links the branch to its issue. The short description is kebab-case and brief.
Examples: feature/42-add-client-revenue-view, bug/57-fix-null-handling-in-dag, spike/61-evaluate-cube-vs-dbt-metrics, chore/65-cleanup-legacy-views.
How to Create a Branch¶
There are three ways to create a branch linked to an issue. Use Option A when possible.
Option A — GitHub UI (Preferred)¶
On the issue page, click "Create a branch" in the right sidebar under Development.
GitHub proposes a name matching the convention (feature/42-short-description), you confirm,
then fetch locally:
This is preferred because it links the branch to the issue automatically with zero extra steps.
Option B — Local branch with issue number in name (Acceptable)¶
GitHub auto-detects the issue number in the branch name and links them. Works fine, just one more step than Option A.
Option C — Branch without issue number (Avoid)¶
If you've already done this, bring it forward — tell the team and the code owner before opening a PR so the issue can be linked manually. Don't open a PR from an unlinked branch without flagging it first.
Pull Request Rules¶
Every PR must:
- Reference its issue in the body:
Closes #N(this triggers GitHub's auto-close automation — never close issues manually if work was done) - Have an area label — either on the PR itself or on the linked issue (enforced by the
label-check.ymlCI workflow) - Use the PR template (
.github/pull_request_template.md) - Require at least 1 review before merge
The PR template includes a testing checklist tailored to the repo: type checks (ty), linting (ruff), DAG validation, dbt compilation, and Snowflake impact assessment.
Closing without a PR is only appropriate when no work was done: the issue was rejected before starting, cancelled, or is a duplicate. Always add a comment explaining why before manually closing.
Sprint Rhythm¶
Sprints are biweekly (two weeks). The Sprint Board view (filtered to iteration: current) is the daily driver.
Monday — Sprint planning. Review the Refined column. Pull items into the current iteration based on priority and team capacity. Assign owners.
Daily — Async standup. Update issue status. Move items between columns as work progresses. Flag blockers by commenting with what's blocking and moving to the Blocked status on the board.
Thursday — Mid-sprint check. Is anything blocked? Is scope creeping? Are there In Progress items with no recent updates?
Friday — Sprint close. Move unfinished items back to Refined (they'll be pulled into the next sprint on Monday if still relevant). Add retro notes as a comment on the sprint or in a team thread.
Tracking Larger Projects¶
For initiatives spanning multiple sprints (like XOOS embedded data views):
- Create an Epic issue with child task list
- Create a Milestone in the repo (e.g.,
XOOS) for external commitments with target dates - Assign child issues to the milestone
- Use the area-filtered Project View to track progress
Milestones track external commitments with due dates. The Epic issue tracks internal progress via its task list.
Progress is visible at three levels: the Epic's task list completion percentage, the Milestone's issue count and burndown, and the Roadmap view's timeline visualization.
Where Claude Fits In¶
Claude participates through three surfaces, each with different strengths:
Claude Chat (claude.ai) — Planning and drafting. Best for discussing requirements, drafting issue descriptions, reviewing documentation, and answering questions about the codebase. Chat has the project context and can produce well-structured issue bodies, ADR drafts, and sprint planning recommendations.
Claude Cowork (desktop) — File-level work. Best for editing configs, writing templates, generating documentation, and batch file operations. Cowork creates and edits files in the local filesystem. It also has the skill-creator skill for building new Claude skills.
Claude Code (terminal) — Execution. Best for running gh commands to create issues, manage labels, open PRs, and query project state. Claude Code has the github-ops skill (.claude/skills/github-ops/) which provides structured operations for all GitHub project management tasks.
The handoff model: Chat and Cowork plan and draft. Code executes. This keeps destructive actions under developer control in the terminal where they can be reviewed before confirming.
Common Scenarios¶
Starting a new feature: Describe the feature to Claude → Claude drafts the issue body → you review → Claude Code creates the issue via gh → Claude Code creates the branch → you work on code → Claude Code opens the PR.
Sprint planning: Open Claude with backlog context → Claude helps prioritize and suggests effort estimates → you confirm → Claude Code batch-updates issues with iteration, priority, and effort fields.
Writing an ADR: Create a spike issue → Claude helps research → Cowork drafts the ADR in docs/decisions/ → you review and iterate → Claude Code commits and opens PR.
Reviewing sprint health: Ask Claude Code "sprint status" → Claude queries open issues for current iteration → reports what's in progress, blocked, or unassigned → flags issues missing labels or acceptance criteria.
Custom Project Fields¶
These fields extend every issue with data-team-specific metadata. They're set in the GitHub Project (not as issue labels).
Priority (P1 through P4) — How urgently this should be addressed. Set as a project field for board sorting and filtering.
Domain — Which client this work relates to: WarbyParker, CondeNast, or unset (internal platform work).
Effort — Size estimate: XS (< 2h), S (half day), M (1-2 days), L (3-5 days), XL (> 1 week). Used for sprint capacity planning.
Iteration — Which biweekly sprint this issue belongs to. Auto-generates date ranges.
Related Files¶
- Issue templates:
.github/ISSUE_TEMPLATE/(feature, bug, spike, elt-pipeline) - PR template:
.github/pull_request_template.md - Label check CI:
.github/workflows/label-check.yml - Claude skill:
.claude/skills/github-ops/SKILL.md - Label taxonomy:
.claude/skills/github-ops/references/workflow-context.md - Team handle mapping:
.claude/skills/github-ops/references/team.md - CLI helper:
.claude/skills/github-ops/scripts/gh_helper.py - Setup checklist:
.claude/ongoing/projects/github-projects-setup-checklist.md