Manages GitHub workflows for data engineering teams using GitHub CLI. Use when creating branches, committing changes, pushing code, creating PRs, or troubleshooting GitHub Actions failures. All PRs target main by default. Only target staging when the user explicitly requests it. Enforces branch naming conventions and keeps PRs clean.
Before ANY git operation, always run the pre-flight check:
# 1. Verify gh CLI authentication
gh auth status
# 2. Get authenticated username (needed for commit verification)
gh api user --jq '.login'
# 3. Read repository context
cat .github/README.md 2>/dev/null || echo "No .github README"
ls -la .github/workflows/ 2>/dev/null
cat CLAUDE.md 2>/dev/null | grep -A 50 -i "git\|github\|branch\|commit" || echo "No git section in CLAUDE.md"
CLAUDE.md or .claude/ folders.github/ - this folder is read-only.md files other than a DAG-specific README.md# Start interactive authentication if not logged in
gh auth login
# Protocol:
# 1. Select GitHub.com
# 2. Select SSH (preferred) or HTTPS
# 3. Select "Login with a web browser"
Add to ~/.zshrc:
if type gh &>/dev/null; then
eval "$(gh completion -s zsh)"
fi
Reload:
source ~/.zshrc
⚠️ IMPORTANT: All PRs target
mainby default.Only create a PR targeting
stagingif the user explicitly says they want to target staging (e.g., "create a PR to staging", "merge this to staging first", "I want this in staging").
| Type | Branch Prefix | Use Case |
|---|---|---|
| Feature | feature/ | New features, enhancements, new DAGs |
| Fix | fix/ | Bug fixes, hotfixes, production issues |
Both branch types target main unless the user explicitly requests staging.
Use this workflow for all changes. PRs always go to main unless explicitly told otherwise.
# 1. Fetch latest and checkout main
git fetch origin main
git checkout main
git pull origin main
# 2. Create branch (feature/ for new work, fix/ for bug fixes)
git checkout -b feature/{descriptive-name}
# OR
git checkout -b fix/{component}-{brief-description}
# 3. Make changes, commit, and push
git add <files>
git commit -m "Brief description of change"
git push -u origin feature/{descriptive-name}
# 4. Create PR targeting MAIN (default)
gh pr create --base main --title "Add feature" --body "Description"
| Scenario | Branch Name |
|---|---|
| New DAG | feature/twilio-genesys-to-snowflake |
| New feature to existing DAG | feature/dag-name-add-retry-logic |
| Bug fix | fix/plan-builder-nightly-api-token |
| Refactoring | feature/refactor-customer-dim-logic |
If (and only if) the user explicitly says they want to target staging:
# Create PR targeting STAGING (only when explicitly requested)
gh pr create --base staging --title "Add feature" --body "Description"
Trigger phrases that indicate staging:
If none of these phrases appear, always use main.
For bug fixes and urgent production issues:
# 1. If you have uncommitted changes you want to preserve, save them
git diff path/to/file > /tmp/fix.patch
# 2. Discard local changes and checkout fresh main
git checkout .
git fetch origin main
git checkout main
git pull origin main
# 3. Create fix branch from main
git checkout -b fix/{dag-or-component-name}-{brief-description}
# 4. Apply fix (either manually or from patch)
git apply /tmp/fix.patch # if using patch
# OR make changes directly
# 5. Commit and push
git add <files>
git commit -m "Fix: brief description of the fix"
git push -u origin fix/{branch-name}
# 6. Create PR targeting MAIN
gh pr create --base main --title "Fix: description" --body "## Summary
- What was broken
- What was fixed
## Test plan
- [x] Verified fix locally"
| Scenario | Branch Prefix | Target |
|---|---|---|
| New DAG | feature/ | main |
| New feature to existing DAG | feature/ | main |
| DAG is broken in production | fix/ | main |
| Configuration/credentials fix | fix/ | main |
| Refactoring | feature/ | main |
| User explicitly requests staging | feature/ or fix/ | staging |
If working from a GitHub issue, include the issue number:
# Get issue title and slugify
ISSUE_NUM=690
ISSUE_TITLE=$(gh issue view $ISSUE_NUM --json title --jq '.title')
SLUG=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9 -]//g' | tr ' ' '-' | sed 's/-\+/-/g' | sed 's/^-\|-$//g')
BRANCH_NAME="feature/${SLUG}-${ISSUE_NUM}"
git checkout -b $BRANCH_NAME
Example: Issue "LSA automation improvements #690"
→ Branch: feature/lsa-automation-improvements-690
See BRANCH_NAMING.md for slugification rules and edge cases.
Only commit files modified during this Claude Code session. Never include unrelated changes.
# 1. Review what changed
git status
git diff
# 2. Stage ONLY files you modified (explicit paths, never `git add .`)
git add path/to/file1.py path/to/file2.sql
# 3. Verify staged changes are yours
git diff --cached --stat
# 4. Commit with brief message
git commit -m "Brief description of change"
Commit message guidelines:
Before pushing, verify no foreign commits are included.
# 1. Get your GitHub username
GH_USER=$(gh api user --jq '.login')
# 2. Check commits on this branch not on default branch
DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name')
git log origin/$DEFAULT_BRANCH..HEAD --format="%an" | sort -u
# 3. If ALL commits show your username (or mapped git name), proceed
git push -u origin HEAD
If foreign commits are detected: Do NOT push. Inform the user that the branch contains commits from other authors and ask how to proceed (likely need to reset and cherry-pick).
# 1. Read workflow triggers to understand what CI runs
cat .github/workflows/*.yml | grep -A 5 "on:" | head -40
# 2. Create PR to MAIN (default - always use main unless user explicitly requests staging)
gh pr create --base main --fill
# Or with explicit title/body:
gh pr create --base main --title "Brief title" --body "Description of changes"
After PR creation:
gh run list --limit 5Each repo has different CI checks and merge permissions. Always identify which repo you're in before deciding the post-PR workflow.
| Repository | CI Checks | Merge Rule | Post-PR Action |
|---|---|---|---|
data-dbt | None | Code owner approval required (aptive-env/data team) | Wait for approval, then merge with gh pr merge --merge --delete-branch |
data-airflow | flake8, detect-secrets, mypy | Human review required | Monitor CI with gh pr checks, fix failures, stop at PR |
data-de-martech | terraform fmt, terraform validate | Human review required | Monitor CI with gh pr checks, fix failures, stop at PR |
data-de-coms | terraform fmt, terraform validate | Human review required | Monitor CI with gh pr checks, fix failures, stop at PR |
data-dbt (legacy note) | None | Previously could self-merge with --admin | As of Feb 2026, claude-config-protection ruleset enforces code owner review |
data-dbt has a claude-config-protection ruleset on main that requires code owner review from aptive-env/data. No CI checks run, so after approval the PR can be merged immediately.
# 1. Create PR
gh pr create --base main --title "feat: description" --body "..."
# 2. Wait for code owner approval (notify user to request approval)
gh pr view <PR-NUMBER> --json reviews --jq '.reviews[] | {author: .author.login, state: .state}'
# 3. After approval, merge
gh pr merge <PR-NUMBER> --merge --delete-branch
If the approver merges via the GitHub UI, the PR will already be merged. Check first:
gh pr view <PR-NUMBER> --json state --jq '.state'
These repos have CI checks that must pass. After creating the PR, monitor checks and fix any failures. Do NOT merge — a human reviewer will handle that.
# 1. Create PR
gh pr create --base main --title "feat: description" --body "..."
# 2. Monitor CI checks (poll until complete)
gh pr checks <PR-NUMBER> --watch
# 3. If checks fail, get details
gh run list --limit 5
gh run view <RUN_ID> --log-failed
# 4. Fix failures, push, re-check
git add <fixed-files> && git commit -m "fix: CI failure" && git push
gh pr checks <PR-NUMBER> --watch
# 5. Stop here — human review required for merge
gh# Checkout & switch to a PR branch
gh pr checkout <PR-NUMBER>
# Check PR status and CI
gh pr checks <PR-NUMBER>
# Watch CI checks until they complete
gh pr checks <PR-NUMBER> --watch
# View PR review status
gh pr view <PR-NUMBER> --json reviews --jq '.reviews[] | {author: .author.login, state: .state}'
# Merge (only for data-dbt after approval)
gh pr merge <PR-NUMBER> --merge --delete-branch
# Fork and clone
gh repo fork <org>/<repo> --clone
# Sync fork with upstream
gh repo sync
These requirements are mandatory for /home/aptive/airflow/data-airflow. Do not open or merge PRs until they are met.
Flake8 (Required):
pip install flake8
flake8
GitHub Actions runs flake8 and will reject PRs with lint errors.
Mypy (Required):
pip install mypy
mypy <paths-you-touched>
Run mypy on the files you changed (e.g., dags/, scripts/, common/). This is mandatory even if CI does not run it yet.
Secrets Scan (Required):
pip install detect-secrets
detect-secrets scan > detect-secrets-output.json
CI runs detect-secrets (uses .secrets.baseline if present) and will fail PRs if new secrets are detected.
Branching Rules (Required):
main for standard work.staging only for extended testing needs.main or staging only.staging auto-resets to main every Wednesday at 5AM UTC.main-based branch before merging to main.Creating a PR is not the finish line. Follow the repo-specific workflow (see "Repository-Specific Merge Rules" above).
For repos with CI checks (data-airflow, data-de-martech, data-de-coms):
main or staging).gh pr checks <PR-NUMBER> --watchgh pr checks --watch after each push.For data-dbt (no CI checks):
main.aptive-env/data.gh pr merge <PR-NUMBER> --merge --delete-branchgh pr view <PR-NUMBER> --json state.When the PR is behind the default branch:
git fetch origin
git rebase origin/main
# If conflicts occur, resolve them, then:
git rebase --continue
# Force push (safe for feature branches)
git push --force-with-lease
# Interactive mode on last N commits
git rebase -i HEAD~5
# OR rebase onto main
git rebase -i $(git merge-base HEAD main)
# Operations:
# p, pick = use commit
# r, reword = use commit, edit message
# s, squash = use commit, meld into previous
# d, drop = remove commit
git push --force-with-lease
# Create a parallel worktree
git worktree add ../hotfix-branch hotfix/critical-bug
# Remove worktree when complete
git worktree remove ../hotfix-branch
git worktree prune
# Apply single commit
git cherry-pick <COMMIT-HASH>
# Apply range (A exclusive, B inclusive)
git cherry-pick <HASH-A>..<HASH-B>
# Conflict resolution
git cherry-pick --continue
# OR
git cherry-pick --abort
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Mark each tested revision as bad or good
git bisect bad
git bisect good
git bisect reset
Before creating a PR, understand what CI/CD will trigger:
# List all workflows
gh workflow list
# View specific workflow triggers
cat .github/workflows/{workflow-name}.yml | grep -A 10 "on:"
Common triggers to know:
on: pull_request - Runs on PR creation/updateon: push: branches: [main] - Runs after merge to mainon: workflow_dispatch - Manual trigger onlySee TROUBLESHOOTING.md for diagnosis steps.
Key principle: The workflows themselves are correct. If Actions fail, it's due to:
Never edit files in .github/ - this folder is read-only.
Before every push, verify:
main (unless user explicitly requested staging)# View the reflog
git reflog
# Recover a lost commit
git checkout <LOST-HASH>
git branch recovered-branch
# Reset hard to a point in time
git reset --hard HEAD@{10.minutes.ago}
Add to ~/.zshrc for speed:
# GH Aliases
alias gprc='gh pr create'
alias gprm='gh pr merge -s -d'
alias gprv='gh pr view --web'
alias gco='gh pr checkout'
# Git Advanced Aliases
alias grb='git rebase -i'
alias gcp='git cherry-pick'
alias gwta='git worktree add'
alias gl='git log --graph --oneline --decorate'
alias gfl='git push --force-with-lease'
Raise to the user for boss escalation when:
Provide specific error messages and your diagnosis when escalating.
Reference this when GitHub Actions fail after a push or PR.
# List recent workflow runs
gh run list --limit 10
# Get details of failed run (replace RUN_ID)
gh run view RUN_ID
# Get full logs
gh run view RUN_ID --log-failed
| Failure Type | Symptoms | Action |
|---|---|---|
| Code Issue | Test failures, lint errors, type errors, build failures referencing your files | Fix the code and push again |
| Dependency Issue | Package install failures, version conflicts | Check if you changed requirements/dependencies; fix and retry |
| Workflow Config Issue | YAML syntax errors, invalid action references, missing secrets | Escalate - workflow files are read-only |
| Infrastructure Issue | Runner failures, timeout, external service down | Escalate - not code-related |
| Flaky Test | Same test passes/fails randomly | Retry once with gh run rerun RUN_ID --failed; escalate if persists |
# 1. Identify the failing file(s) from logs
gh run view RUN_ID --log-failed | grep -E "(Error|FAILED|error:)"
# 2. Fix the code
# 3. Commit and push
git add <fixed-files>
git commit -m "Fix: <brief description of fix>"
git push
Provide the user with:
gh run view RUN_ID --web (or provide URL)Example escalation message:
GitHub Actions failed due to a workflow configuration issue.
- Run: https://github.com/org/repo/actions/runs/123456
- Error: Secret `DEPLOY_KEY` is not set
- Assessment: This requires adding a repository secret, which is outside my scope.
Please escalate to your team lead to resolve the secret configuration.
task-complete-notify skill to inform the user the GIT tasks are complete, whether the issue was resolved or escalated.| Error Pattern | Likely Cause | Fix |
|---|---|---|
ModuleNotFoundError | Missing dependency | Add to requirements.txt and push |
sqlfluff / black / ruff failures | Linting | Run linter locally, fix, push |
pytest failures | Test regression | Fix test or code, push |
dbt build failures | Model error | Check SQL/YAML, fix, push |
| Connection timeout to database | CI environment issue | Escalate |
# Rerun only failed jobs (faster)
gh run rerun RUN_ID --failed
# Rerun entire workflow
gh run rerun RUN_ID
Only rerun without code changes if you suspect a flaky test or transient infrastructure issue. If it fails twice the same way, it's not flaky.
feature/{slugified-description}
feature/{slugified-issue-title}-{issue-number}
fix/{component}-{brief-description}
| Description/Issue Title | Issue # | Branch Name |
|---|---|---|
| LSA automation improvements | 690 | feature/lsa-automation-improvements-690 |
| Fix NULL handling in customer_dim | 142 | fix/customer-dim-null-handling |
| Add dbt tests for sales_fact | 88 | feature/add-dbt-tests-for-sales-fact-88 |
| Pipeline timeout issue | 501 | fix/pipeline-timeout-issue |
| New Twilio DAG | — | feature/twilio-genesys-to-snowflake |
# Get issue title and number, then slugify
ISSUE_NUM=690
ISSUE_TITLE=$(gh issue view $ISSUE_NUM --json title --jq '.title')
SLUG=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9 -]//g' | tr ' ' '-' | sed 's/-\+/-/g' | sed 's/^-\|-$//g')
BRANCH_NAME="feature/${SLUG}-${ISSUE_NUM}"
echo $BRANCH_NAME
Very long titles: If the branch name exceeds 100 characters, truncate the slug portion (keep the issue number):
# Truncate slug to keep total under 100 chars
MAX_SLUG_LEN=$((100 - ${#ISSUE_NUM} - 9)) # 9 = "feature/-" + "-"
SLUG="${SLUG:0:$MAX_SLUG_LEN}"
No associated issue: If working without an issue, use a descriptive slug without a number:
feature/descriptive-task-name
But prefer creating an issue first for traceability.
Test with these prompts to verify the skill triggers correctly:
Scenario: DAG plan_builder_nightly was failing with 401 errors.
Steps taken:
# 1. Save the fix as a patch (had uncommitted changes on another branch)
git diff dags/plan_builder_nightly/src/api_client.py > /tmp/api_client_fix.patch
# 2. Discard local changes, checkout main
git checkout .
git fetch origin main
git checkout main
git pull origin main
# 3. Create fix branch
git checkout -b fix/plan-builder-nightly-api-token
# 4. Apply the patch
git apply /tmp/api_client_fix.patch
# 5. Commit and push
git add dags/plan_builder_nightly/src/api_client.py
git commit -m "Fix plan_builder_nightly API token retrieval"
git push -u origin fix/plan-builder-nightly-api-token
# 6. Create PR to main
gh pr create --base main --title "Fix plan_builder_nightly API token retrieval" --body "..."
Result: PR #734 created targeting main, CI passed, ready for review.
Scenario: New DAG needed for Twilio data integration.
Steps taken:
# 1. Checkout main
git checkout main
git pull origin main
# 2. Create feature branch
git checkout -b feature/twilio-genesys-to-snowflake
# 3. Develop the feature, commit
git add dags/twilio_genesys_to_snowflake/
git commit -m "Add twilio_genesys_to_snowflake DAG"
git push -u origin feature/twilio-genesys-to-snowflake
# 4. Create PR to main (default)
gh pr create --base main --title "Add twilio_genesys_to_snowflake DAG" --body "..."
Result: PR #730 created targeting main.