tdd:ci
SkillDev toolsCI-driven TDD workflow - commit, local checks, push, wait for CI, iterate on failures
Available today. Use it from your connected AI after setup.
No other account needed.
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:
- Note the log file path and exit code
- Use
Task(subagent_type='Explore')to read the log and extract relevant info - The subagent returns a concise summary (errors, unexpected output, specific data)
- Use subagents for success analysis too — e.g., "verify lint passed with no warnings"
- Fix or proceed based on the summary
tdd:ci vs tdd:hypershift
| Aspect | tdd:ci | tdd:hypershift |
|---|---|---|
| Test environment | CI pipeline (no direct access) | Your own HyperShift cluster |
| Debugging | Analyze CI logs after failure | Real-time debugging with k8s:* skills |
| Feedback loop | Slower (wait for CI) | Faster (immediate cluster access) |
| Use when | Final validation, no cluster | Active 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
-
Parse the issue/PR from the URL argument:
- Extract repo owner/name and issue/PR number
- Fetch the issue/PR details with
gh issue vieworgh pr view
-
Fetch upstream main:
git fetch upstream main -
Check for existing worktree for this issue/PR:
git worktree listLook for branches containing the issue number (e.g.
fix/keycloak-login-652). -
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 -
If a worktree already exists, confirm with the user whether to reuse it.
-
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
-
Read the issue — understand what's reported, what's expected, reproduction steps
-
Research — investigate the codebase for the root cause:
- Use
rca:ciorrca:kindpatterns to diagnose - Search for the affected component, trace the code path
- Check if tests exist for this scenario
- Use
-
Plan the fix — determine the approach:
- What files need to change
- What tests need to be written or updated
- Are there multiple valid approaches?
-
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]" -
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 fixesfeat:- New featuresdocs:- Documentationrefactor:- Code refactoringtest:- Test changeschore:- 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:
- If
cve:scanreturns clean → proceed to Phase 4 - If
cve:scanfinds HIGH/CRITICAL CVEs →cve:brainstormactivates 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:brainstormfor 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:
- Find the first error
- Check if it's a flaky test or real failure
- Return a 1-3 line summary of root cause
- Use
superpowers:systematic-debuggingfor 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 statek8s:logs- check logs immediatelyk8s:live-debugging- iterative fixes
Quick Reference
| Step | Command |
|---|---|
| Stage files | git add <files> |
| Commit | git commit -s -m "type: message" |
| Lint | make lint |
| Pre-commit | pre-commit run --all-files |
| Push | git push |
| Watch CI | gh pr checks --watch |
| View failure | gh run view <id> --log-failed |
Task Tracking
On invocation:
- TaskList - check existing tasks for this worktree/issue
- 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
- 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't | Do Instead |
|---|---|
| Commit to a branch with an unrelated open PR | Run branch verification gate, create worktree |
| Work on current branch when given an issue URL | Create a worktree from upstream/main |
| Push without local checks | Run make lint and pre-commit first |
| Amend after push | Create new commits |
| Push multiple times quickly | Wait for CI between pushes |
| Guess at fixes | Analyze failure logs first |
| Skip brainstorming | Use /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:
- The skill auto-detects the current session ID and PR number
- Posts a session report comment with token usage, skills used, and workflow diagram
- 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 worktreessuperpowers:brainstorming- Design before implementationsuperpowers:systematic-debugging- Debug CI failuressuperpowers:verification-before-completion- Verify before claiming donetdd:hypershift- TDD with HyperShift clustersgit:status- Check worktree and PR status before pushingtest:review- Review test qualitytest:write- Write new testsgit:commit- Commit format and conventionsgit:rebase- Rebase onto upstream mainsession:post- Post session analytics to PRcve: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