tdd:ci

SkillDev tools

CI-driven TDD workflow - commit, local checks, push, wait for CI, iterate on failures

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the tdd:ci skill

What this skill tells your AI

The instructions your AI receives, as published by rossoctl/rossoctl in .claude/skills/tdd:ci/SKILL.md and read by ahel’s review.

Table of Contents

  • tdd:ci vs tdd:hypershift
  • When to Use
  • The Workflow
  • Phase 0: Worktree Setup
  • Phase 0b: Research & Plan
  • Phase 1: Brainstorm
  • Phase 2: Commit
  • Phase 3: Local Checks
  • Phase 3.5: CVE Gate
  • Phase 4: Push to PR
  • Phase 5: Wait for CI
  • Phase 6: Analyze Failures
  • Phase 7: Fix and Iterate
  • Phase 8: Handle PR Reviews
  • Escalation
  • Task Tracking
  • Anti-Patterns
  • Related Skills

Iterative development workflow using CI as the test environment. Commit changes, run local checks, push to PR, wait for CI results, and iterate on failures.

Context-Safe Execution (MANDATORY)

Every command that produces more than ~5 lines of output MUST redirect to a file.

# Session-scoped log directory — use worktree/repo name to avoid collisions
# For parallel worktree sessions, pre-set LOG_DIR to a unique path
export LOG_DIR="${LOG_DIR:-${WORKSPACE_DIR:-/tmp}/rossoctl-tdd}"
mkdir -p "$LOG_DIR"

Key Patterns

# CI log download — ALWAYS to file, NEVER into context
gh run view <run-id> --log-failed > $LOG_DIR/ci-failed.log 2>&1; echo "EXIT:$?"
# Analyze failures in subagent: Task(subagent_type='Explore') to read $LOG_DIR/ci-failed.log

# Local checks — redirect output
make lint > $LOG_DIR/lint.log 2>&1 && echo "OK: lint passed" || echo "FAIL (see $LOG_DIR/lint.log)"
pre-commit run --all-files > $LOG_DIR/precommit.log 2>&1 && echo "OK: pre-commit passed" || echo "FAIL (see $LOG_DIR/precommit.log)"
uv run pytest rossoctl/tests/ -v --ignore=rossoctl/tests/e2e > $LOG_DIR/unit-tests.log 2>&1 && echo "OK: tests passed" || echo "FAIL (see $LOG_DIR/unit-tests.log)"

Log Analysis Rule

NEVER read large log files in the main context. Always use subagents:

  1. Note the log file path and exit code
  2. Use Task(subagent_type='Explore') to read the log and extract relevant info
  3. The subagent returns a concise summary (errors, unexpected output, specific data)
  4. Use subagents for success analysis too — e.g., "verify lint passed with no warnings"
  5. Fix or proceed based on the summary

tdd:ci vs tdd:hypershift

Aspecttdd:citdd:hypershift
Test environmentCI pipeline (no direct access)Your own HyperShift cluster
DebuggingAnalyze CI logs after failureReal-time debugging with k8s:* skills
Feedback loopSlower (wait for CI)Faster (immediate cluster access)
Use whenFinal validation, no clusterActive development, need to inspect state

Use tdd:hypershift when you have a cluster and need real-time debugging. Use tdd:ci when iterating on CI failures or for final PR validation.

When to Use

  • Iterating on CI failures (no cluster access needed)
  • Final validation before merge
  • When you don't have a HyperShift cluster running
  • Simple changes that don't need live debugging

The Workflow

flowchart TD
    START(["/tdd:ci"]) --> HAS_URL{"Has GH URL?"}
    HAS_URL -->|Yes| P0["Phase 0: Worktree Setup"]:::tdd
    HAS_URL -->|No| P1

    P0 --> IS_ISSUE{"Is issue URL?"}
    IS_ISSUE -->|Yes| P0B["Phase 0b: Research & Plan"]:::tdd
    IS_ISSUE -->|No, PR| P1

    P0B --> P1["Phase 1: Brainstorm"]:::tdd
    P1 --> P2["Phase 2: Commit"]:::git
    P2 --> P3["Phase 3: Local Checks"]:::test
    P3 -->|Checks fail| P2
    P3 -->|Checks pass| P3B["Phase 3.5: CVE Gate"]:::cve
    P3B -->|Clean| P4["Phase 4: Push to PR"]:::git
    P3B -->|CVE found| CVE_HOLD["cve:brainstorm (BLOCKS push)"]:::cve
    CVE_HOLD -->|Resolved| P4
    P4 --> P5["Phase 5: Wait for CI"]:::ci
    P5 --> RESULT{"CI Result?"}

    RESULT -->|Pass| P8["Phase 8: Handle Reviews"]:::tdd
    RESULT -->|Fail| P6["Phase 6: Analyze Failures"]:::rca

    P6 --> FAILCOUNT{"3+ failures?"}
    FAILCOUNT -->|No| P7["Phase 7: Fix & Iterate"]:::tdd
    FAILCOUNT -->|Yes| ESCALATE["Escalate to tdd:hypershift"]:::hypershift

    P7 --> P2

    P8 -->|Changes needed| P2
    P8 -->|Approved| DONE([Merged])

    ESCALATE --> HS["tdd:hypershift"]:::tdd

    classDef tdd fill:#4CAF50,stroke:#333,color:white
    classDef rca fill:#FF5722,stroke:#333,color:white
    classDef git fill:#FF9800,stroke:#333,color:white
    classDef k8s fill:#00BCD4,stroke:#333,color:white
    classDef hypershift fill:#3F51B5,stroke:#333,color:white
    classDef ci fill:#2196F3,stroke:#333,color:white
    classDef test fill:#9C27B0,stroke:#333,color:white
    classDef cve fill:#D32F2F,stroke:#333,color:white

Follow this diagram as the workflow.

Phase 0: Worktree Setup (when linked to GH issue/PR)

This phase is MANDATORY when tdd:ci is invoked with a GitHub issue or PR URL.

When the skill receives a GitHub URL (issue or PR), the first step is always to set up an isolated worktree based on upstream/main. This ensures the fix is developed against the latest upstream code, not against a local feature branch.

Steps

  1. Parse the issue/PR from the URL argument:

    • Extract repo owner/name and issue/PR number
    • Fetch the issue/PR details with gh issue view or gh pr view
  2. Fetch upstream main:

    git fetch upstream main
    
  3. Check for existing worktree for this issue/PR:

    git worktree list
    

    Look for branches containing the issue number (e.g. fix/keycloak-login-652).

  4. If no worktree exists, ask the user for a worktree name (suggest a default based on the issue, e.g. fix-652), then create it:

    git worktree add .worktrees/<name> -b fix/<slug>-<number> upstream/main
    
  5. If a worktree already exists, confirm with the user whether to reuse it.

  6. All subsequent phases operate inside the worktree. File reads, edits, commits, and pushes all happen under .worktrees/<name>/.

Branch naming convention

  • Issues: fix/<slug>-<number> (e.g. fix/keycloak-login-652)
  • PRs: reuse the existing PR branch

Example: Clear fix

/tdd:ci https://github.com/rossoctl/rossoctl/issues/652

-> Fetch upstream main
-> No existing worktree for #652
-> Ask user: "Worktree name?" (default: fix-652)
-> git worktree add .worktrees/fix-652 -b fix/keycloak-login-652 upstream/main
-> Phase 0b: Research codebase, find root cause
-> Post to issue: "Root cause is X. I'll fix by Y. Creating PR."
-> Implement fix in .worktrees/fix-652/
-> Commit, push, create PR against upstream/main

Example: Unclear approach — post questions and wait

/tdd:ci https://github.com/rossoctl/rossoctl/issues/678

-> Fetch upstream main, create worktree
-> Phase 0b: Research reveals two possible causes
-> Post to issue:
     "## Investigation
      I found two potential root causes:
      1. Keycloak redirect URL mismatch — backend uses external URL for JWKS
      2. Role mapping — admin role not mapped to rossoctl-admin

      ## Questions
      - Should the backend use an in-cluster URL for JWKS, or fix DNS?
      - Is the admin→rossoctl-admin role mapping intentional?

      Waiting for clarification before proceeding."
-> STOP — wait for issue author to respond
-> (Author replies: "Option 1 for JWKS, role mapping is a bug")
-> Resume: implement fix based on response
-> Commit, push, create PR

Example: Multiple approaches — present options

/tdd:ci https://github.com/rossoctl/rossoctl/issues/700

-> Research reveals the fix can go two ways
-> Post to issue:
     "## Proposed approaches
      1. **Add KEYCLOAK_INTERNAL_URL env var** — explicit, works everywhere,
         but adds configuration surface
      2. **Auto-detect in-cluster via KUBERNETES_SERVICE_HOST** — zero config,
         but implicit and harder to debug

      Which approach do you prefer?"
-> STOP — wait for response before coding

Phase 0b: Research & Plan (when working from a GH issue)

This phase applies to ALL /tdd skills when invoked with a GH issue URL. Before writing any code, investigate the issue and present findings.

Steps

  1. Read the issue — understand what's reported, what's expected, reproduction steps

  2. Research — investigate the codebase for the root cause:

    • Use rca:ci or rca:kind patterns to diagnose
    • Search for the affected component, trace the code path
    • Check if tests exist for this scenario
  3. Plan the fix — determine the approach:

    • What files need to change
    • What tests need to be written or updated
    • Are there multiple valid approaches?
  4. Post to the issue — before coding, comment on the issue (requires approval):

    ## Investigation
    
    **Root cause**: [what causes the issue]
    **Affected files**: [list]
    
    ## Proposed approach
    
    [If clear approach]: "I plan to fix this by [description]. Will create a PR."
    
    [If multiple options]:
    "I see two approaches:
     1. [approach A] — [tradeoff]
     2. [approach B] — [tradeoff]
     Which approach do you prefer?"
    
    [If unclear]: "I have questions about this issue:
     - [question 1]
     - [question 2]"
    
  5. Wait for response if questions were posted. Proceed with coding if the approach is clear.

Phase 1: Brainstorm (New Features)

For new features or complex changes, use the brainstorming skill first:

/superpowers:brainstorming

This helps clarify:

  • What exactly needs to be done
  • Edge cases and requirements
  • Potential approaches

Phase 2: Commit

Branch verification (first commit only)

Before the first commit, run the Branch Verification Gate (see parent tdd skill):

git branch --show-current
gh pr list --head "$(git branch --show-current)" --json number,title,url --jq '.[] | "#\(.number) \(.title) \(.url)"'

If the current branch has an open PR for different work, stop and create a worktree:

git worktree add .worktrees/<name> -b <new-branch> upstream/main

Create commit

Create a focused commit with your changes:

# Stage specific files (not git add -A)
git add <changed-files>

# Commit with sign-off
git commit -s -m "fix: description of change"

Commit message conventions:

  • fix: - Bug fixes
  • feat: - New features
  • docs: - Documentation
  • refactor: - Code refactoring
  • test: - Test changes
  • chore: - Maintenance

Phase 3: Local Checks

Run local validation before pushing (redirect output to files):

make lint > $LOG_DIR/lint.log 2>&1 && echo "OK: lint" || echo "FAIL: lint (see $LOG_DIR/lint.log)"
pre-commit run --all-files > $LOG_DIR/precommit.log 2>&1 && echo "OK: pre-commit" || echo "FAIL: pre-commit (see $LOG_DIR/precommit.log)"
uv run pytest rossoctl/tests/ -v --ignore=rossoctl/tests/e2e > $LOG_DIR/unit-tests.log 2>&1 && echo "OK: tests" || echo "FAIL: tests (see $LOG_DIR/unit-tests.log)"

Fix any failures before pushing. On failure, use Task(subagent_type='Explore') to read the log file.

Phase 3.5: CVE Gate

MANDATORY before pushing. Scan dependencies for known CVEs before any public output.

Invoke cve:scan on the working tree:

  1. If cve:scan returns clean → proceed to Phase 4
  2. If cve:scan finds HIGH/CRITICAL CVEs → cve:brainstorm activates a CVE hold
    • Do NOT proceed to Phase 4 until the hold is resolved
    • The hold blocks gh pr create, gh pr comment, gh issue create/comment
    • Silent fixes (dependency bumps with neutral commit messages) are allowed
    • See cve:brainstorm for resolution steps

Phase 4: Push to PR

# Push to remote (creates PR if needed)
git push -u origin <branch-name>

# Or if PR exists, just push
git push

If no PR exists yet:

gh pr create --title "fix: description" --body "## Summary
- What this changes

## Test plan
- CI will validate"

Phase 5: Wait for CI

Monitor CI status:

# Watch PR checks
gh pr checks --watch

# Or check specific workflow
gh run list --branch <branch-name>
gh run view <run-id>

Wait for CI to complete before making more changes.

Phase 6: Analyze Failures

When CI fails, download logs to a file — never dump CI logs into main context:

# Download failed run logs to file
gh run view <run-id> --log-failed > $LOG_DIR/ci-run-<run-id>.log 2>&1; echo "EXIT:$?"

# Or view in browser (minimal context — just prints URL)
gh run view <run-id> --web

Analyze in subagent — use Task(subagent_type='Explore') to read $LOG_DIR/ci-run-<run-id>.log and:

  1. Find the first error
  2. Check if it's a flaky test or real failure
  3. Return a 1-3 line summary of root cause
  4. Use superpowers:systematic-debugging for complex failures

Phase 7: Fix and Iterate

# Make fix
vim <file>

# Commit the fix (new commit, not amend)
git add <fixed-files>
git commit -s -m "fix: address CI failure - description"

# Push
git push

# Wait for CI again
gh pr checks --watch

Repeat until all checks pass.

Phase 8: Handle PR Reviews (after CI passes)

When CI passes, check for review comments:

gh pr view <pr-number> --json reviews,comments --jq '.reviews[] | "\(.author.login): \(.state) - \(.body[:100])"'
gh api repos/rossoctl/rossoctl/pulls/<pr-number>/comments --jq '.[] | "#\(.id) @\(.user.login) [\(.path):\(.line)] \(.body[:100])"'

For each review comment, determine:

Review comment received
    │
    ├─ Clear actionable feedback → Implement as a NEW commit
    │   (one commit per logical review item, not one per comment)
    │
    ├─ Ambiguous or unclear → Comment back on the PR asking
    │   for clarification with specific questions
    │
    ├─ Disagree with suggestion → Comment back explaining why,
    │   offer alternative approach with evidence
    │
    └─ Multiple options possible → Comment with options:
        "I see two approaches for this:
         1. [approach A] — [tradeoff]
         2. [approach B] — [tradeoff]
         Which do you prefer?"

Commit review fixes

Each logical review item gets its own commit:

git add <files>
git commit -s -m "fix: address review - <what changed>"

After addressing all comments, push and comment on the PR:

git push

Then summarize what was addressed in a PR comment (requires approval):

Addressed review feedback:
- commit abc123: [what was changed for comment X]
- commit def456: [what was changed for comment Y]
- Replied to comment Z with question about [topic]

Then wait for CI again (back to Phase 5)

After pushing review fixes, wait for CI to pass, then check for new review comments. Repeat until approved.

Escalation: Too Many Iterations?

After 3+ failed CI iterations, consider switching to tdd:hypershift for real-time debugging:

Check for Existing Cluster

# Check if cluster exists for current worktree
WORKTREE=$(basename "${WORKSPACE_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}")
ls ~/clusters/hcp/rossoctl-hypershift-custom-*/auth/kubeconfig 2>/dev/null

Decision Tree

CI failed 3+ times?
    │
    ├─ YES → Check for existing cluster
    │         │
    │         ├─ Cluster exists → Switch to `tdd:hypershift`
    │         │
    │         └─ No cluster → Ask user:
    │                         "Create HyperShift cluster for debugging?"
    │                         │
    │                         ├─ YES → Use `hypershift:cluster` to create
    │                         │        Then switch to `tdd:hypershift`
    │                         │
    │                         └─ NO → Continue with tdd:ci
    │
    └─ NO → Continue iterating

Escalate to HyperShift

If user approves cluster creation:

# Create cluster (max 5 char suffix)
KUBECONFIG=~/clusters/hcp/rossoctl-hypershift-custom-<suffix>/auth/kubeconfig \
  ./.github/scripts/local-setup/hypershift-full-test.sh <suffix> \
  --include-cluster-create --skip-cluster-destroy

Then switch to tdd:hypershift for real-time debugging with:

  • k8s:pods - inspect pod state
  • k8s:logs - check logs immediately
  • k8s:live-debugging - iterative fixes

Quick Reference

StepCommand
Stage filesgit add <files>
Commitgit commit -s -m "type: message"
Lintmake lint
Pre-commitpre-commit run --all-files
Pushgit push
Watch CIgh pr checks --watch
View failuregh run view <id> --log-failed

Task Tracking

On invocation:

  1. TaskList - check existing tasks for this worktree/issue
  2. TaskCreate with naming convention:
    • <worktree> | <PR> | <plan-doc> | <topic> | <phase> | <task>
    • Example: fix-652 | PR#656 | ad-hoc | Keycloak login | Phase 0 | Create worktree
    • Example: fix-652 | PR#656 | ad-hoc | Keycloak login | Phase 2 | Commit fix
  3. TaskUpdate as each phase completes

Typical task structure for an issue fix:

#1 [completed] fix-652 | none | ad-hoc | Keycloak | Phase 0 | Create worktree
#2 [completed] fix-652 | none | ad-hoc | Keycloak | Investigate | Root cause analysis
#3 [completed] fix-652 | PR#656 | ad-hoc | Keycloak | Phase 2 | Commit fix
#4 [completed] fix-652 | PR#656 | ad-hoc | Keycloak | Phase 4 | Push and create PR
#5 [completed] fix-652 | PR#656 | ad-hoc | Keycloak | Phase 5 | Wait for CI

Anti-Patterns

Don'tDo Instead
Commit to a branch with an unrelated open PRRun branch verification gate, create worktree
Work on current branch when given an issue URLCreate a worktree from upstream/main
Push without local checksRun make lint and pre-commit first
Amend after pushCreate new commits
Push multiple times quicklyWait for CI between pushes
Guess at fixesAnalyze failure logs first
Skip brainstormingUse /superpowers:brainstorming for new features

Troubleshooting

Problem: Worktree branch already exists

Symptom: git worktree add fails with "branch already exists" Fix: Check if worktree already exists with git worktree list. Reuse it or remove with git worktree remove.

Problem: Helm dependency build needed in worktree

Symptom: helm template fails with missing dependencies Fix: Run helm dependency build in the worktree's chart directory.

Problem: CI fails on DCO check

Symptom: DCO check fails on PR Fix: Ensure commits use git commit -s for sign-off.

UI Tests

For Playwright UI tests (login, navigation, agent chat), invoke test:ui. CI runs UI tests automatically via 91-run-ui-tests.sh after pytest E2E tests.

Session Reporting

After the TDD workflow completes (CI green and PR approved/merged), invoke session:post to capture session metadata:

  1. The skill auto-detects the current session ID and PR number
  2. Posts a session report comment with token usage, skills used, and workflow diagram
  3. Updates the pinned summary comment

This is optional but recommended for tracking development effort.

Related Skills

  • test:ui - Write and run Playwright UI tests (CI/Kind/HyperShift)
  • git:worktree - Create and manage git worktrees
  • superpowers:brainstorming - Design before implementation
  • superpowers:systematic-debugging - Debug CI failures
  • superpowers:verification-before-completion - Verify before claiming done
  • tdd:hypershift - TDD with HyperShift clusters
  • git:status - Check worktree and PR status before pushing
  • test:review - Review test quality
  • test:write - Write new tests
  • git:commit - Commit format and conventions
  • git:rebase - Rebase onto upstream main
  • session:post - Post session analytics to PR
  • cve:scan - CVE scanning gate (Phase 3.5)
  • cve:brainstorm - CVE disclosure planning (if CVEs found)

Signals

GitHub stars
300
Forks
107
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
tdd-ci
Source
github.com/rossoctl/rossoctl