| name | test-plan-publish |
| description | Publish test plan artifacts to GitHub — creates a branch, commits all artifacts, and opens a PR with optional reviewer assignment. Use after test plan review to make artifacts available for team collaboration and formal review feedback. |
| argument-hint | [FEATURE_SOURCE] [--repo owner/repo] [--reviewers user1,user2] |
| user-invocable | true |
| model | sonnet |
| allowedTools | ["Read","Bash","AskUserQuestion"] |
Test Plan Publisher
Publish test plan artifacts to GitHub by creating a branch, committing all files, and opening a pull request.
Prerequisites
You must work from a personal fork, not the upstream repository:
- Fork
opendatahub-io/opendatahub-test-plans to your GitHub account
- Clone your fork:
git clone https://github.com/YOUR-USERNAME/opendatahub-test-plans ~/Code/opendatahub-test-plans
- Create test plan:
/test-plan-create RHAISTRAT-XXX
- Generate test cases:
/test-plan-create-cases
- Publish:
/test-plan-publish - creates PR from your fork → upstream
The skill will verify you're working from a fork and fail if you're in the upstream repository.
Usage
/test-plan-publish [FEATURE_DIR] [--repo <owner/repo>] [--reviewers <user1,user2>]
Examples:
/test-plan-publish tool_calling_metadata
/test-plan-publish tool_calling_metadata --reviewers alice,bob
/test-plan-publish tool_calling_metadata --repo org/test-plans-repo
/test-plan-publish (auto-detects feature directory from prior /test-plan-create run)
Inputs
From arguments
Parse $ARGUMENTS to extract:
- First argument (optional): Test plan source - can be:
- Local directory path:
mcp_catalog or /path/to/mcp_catalog
- GitHub branch:
https://github.com/org/repo/tree/test-plan/RHAISTRAT-400
- GitHub PR:
https://github.com/org/repo/pull/5
--repo (optional): Target GitHub repository in owner/repo format. Defaults to the current repository or detected from GitHub URL.
--reviewers (optional): Comma-separated list of GitHub usernames to assign as PR reviewers. If not provided, uses the reviewers field from TestPlan.md frontmatter. If both are empty, no reviewers are assigned.
Auto-detection
If no feature directory is provided and a TestPlan.md was just generated by /test-plan-create in this session, use its feature directory automatically — proceed directly to Step 0.
Interactive fallback
If no feature directory can be determined, ask the user via AskUserQuestion:
Where is the test plan to publish?
You can provide:
- Local directory path (e.g.,
~/Code/opendatahub-test-plans/plans/ai-hub/mcp_catalog)
- GitHub branch URL (e.g.,
https://github.com/org/repo/tree/test-plan/RHAISTRAT-400)
- GitHub PR URL (e.g.,
https://github.com/org/repo/pull/5)
Process
Step 0: Pre-flight Checks
0.0 Python dependencies
Install the test-plan package (makes all scripts importable):
(cd $(git -C ${CLAUDE_SKILL_DIR} rev-parse --show-toplevel) && uv sync --extra dev)
If installation fails, inform the user and do NOT proceed. Once installed, all Python scripts will work from any directory.
0.1 GitHub CLI
Run gh auth status via Bash. If it fails, inform the user that gh CLI must be installed and authenticated. Do NOT proceed until this succeeds.
0.2 Locate Feature Directory
Use the shared locate-feature-dir utility to find the test plan:
result=$(cd $(git -C ${CLAUDE_SKILL_DIR} rev-parse --show-toplevel) && uv run python scripts/repo.py locate-feature-dir "<source>")
if [ $? -ne 0 ]; then
echo "$result"
exit 1
fi
feature_dir=$(echo "$result" | jq -r '.feature_dir')
source_type=$(echo "$result" | jq -r '.source_type')
if [ "$source_type" = "github" ]; then
repo_owner=$(echo "$result" | jq -r '.repo_owner')
repo_name=$(echo "$result" | jq -r '.repo_name')
[ -z "$target_repo" ] && target_repo="$repo_owner/$repo_name"
fi
0.3 Validate artifacts and frontmatter
Run unified validation on the feature directory. This checks structure (TestPlan.md, test_cases/), validates frontmatter for all present artifacts (TestPlan.md, TestPlanGaps.md if exists), and validates test case files (if any). If validation fails, show the errors and do NOT proceed.
(cd $(git -C ${CLAUDE_SKILL_DIR} rev-parse --show-toplevel) && uv run python scripts/validate.py all <feature_dir>)
Check that README.md exists. Do NOT fail if TestPlanGaps.md or test_cases/ are absent — the user may publish before generating test cases.
0.5 Check for clean working state
Check if there are uncommitted changes in the feature directory. This is informational only — warn the user if there are unstaged changes, but do not block.
if git -C <feature_dir> rev-parse --git-dir > /dev/null 2>&1; then
changes=$(git -C <feature_dir> status --porcelain .)
if [ -n "$changes" ]; then
echo "⚠ Uncommitted changes detected:"
echo "$changes"
else
echo "✓ Working directory is clean"
fi
else
echo "ℹ Not a git repository - skipping clean state check"
fi
Note: Use variable name changes instead of status (which is readonly in zsh).
Step 1: Read Metadata
- Read frontmatter from
<feature_dir>/TestPlan.md using Bash:
(cd $(git -C ${CLAUDE_SKILL_DIR} rev-parse --show-toplevel) && uv run python scripts/frontmatter.py read <feature_dir>/TestPlan.md)
- Extract:
source_key, version, feature, reviewers
- Determine the branch name:
test-plan/<source_key> (version not included)
- Example:
test-plan/RHAISTRAT-400
- Rationale: Using version-free branch names allows
/test-plan-update to push updates to the same PR instead of creating new PRs for each version bump
- Determine reviewers: use
--reviewers argument if provided, otherwise use frontmatter reviewers field
Step 1.5: Determine Target Repository
IMPORTANT: Test plans must NOT be published to the skill repository.
-
Export CLAUDE_SKILL_DIR for validation utilities:
export CLAUDE_SKILL_DIR
-
If --repo was provided in arguments:
-
If --repo was NOT provided: Ask user via AskUserQuestion for a text input (NOT a menu):
Where should this test plan be published?
Specify the target GitHub repository in owner/repo format, or press Enter for default: opendatahub-io/opendatahub-test-plans
-
Validate the user-provided or default repository:
-
Store the final validated value in target_repo
Rationale: Prevents accidental (or intentional) publishing of test plans to the skill repository, which would pollute the skill codebase with test plan artifacts and cause branch-switching issues for contributors.
Step 1.6: Verify Working from a Fork
IMPORTANT: Check if the local repository is a personal fork, not the upstream repository.
-
Get and normalize the git remote of the feature directory:
cd <feature_dir>
remote_url=$(git config --get remote.origin.url)
normalized=$(echo "$remote_url" | sed -E 's|\.git$||; s|/+$||')
if ! echo "$normalized" | grep -qE 'github\.com[:/][^/]+/[^/]+$'; then
echo "❌ Cannot parse GitHub repository from remote URL: $remote_url"
exit 1
fi
owner=$(echo "$normalized" | sed -E 's|.*github\.com[:/]([^/]+)/[^/]+$|\1|')
repo=$(echo "$normalized" | sed -E 's|.*github\.com[:/][^/]+/([^/]+)$|\1|')
remote_repo="${owner}/${repo}"
remote_repo_lower=$(echo "$remote_repo" | tr '[:upper:]' '[:lower:]')
-
Check if it's the upstream repository:
if [ "$remote_repo_lower" = "opendatahub-io/opendatahub-test-plans" ]; then
echo "❌ You are working directly in the upstream repository."
1
Rationale: Users cannot push branches directly to opendatahub-io/opendatahub-test-plans. They must work from a fork and create a PR from fork → upstream.
Step 2: Confirm with User
Before creating the branch and PR, present a summary to the user via AskUserQuestion:
Ready to publish test plan
- Feature:
<feature>
- Strategy:
<source_key>
- Version:
<version>
- Branch:
test-plan/<source_key>
- Target repo:
<target_repo>
- Reviewers:
<reviewer list> (or "none")
- Files to publish:
<feature_dir>/TestPlan.md
<feature_dir>/README.md
<feature_dir>/TestPlanGaps.md (if exists)
<feature_dir>/test_cases/ (N files, if exists)
Proceed? (yes/no)
If the user declines, stop.
Step 3: Update Frontmatter
- Bump the
status to In Review:
(cd $(git -C ${CLAUDE_SKILL_DIR} rev-parse --show-toplevel) && uv run python scripts/frontmatter.py set <feature_dir>/TestPlan.md status="In Review")
Step 4: Create Branch and Commit
-
Ensure we're in the correct working directory:
feature_dir_abs=$(cd "$(dirname "$feature_dir")" && pwd)/$(basename "$feature_dir")
publish_repo_root=$(dirname "$feature_dir_abs")
export CLAUDE_SKILL_DIR
current_repo=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
skill_parent="${CLAUDE_SKILL_DIR}/../.."
skill_repo_root=$(cd "$skill_parent" && git rev-parse --show-toplevel 2>/dev/null || echo "")
if [ -n "$current_repo" ] && [ -n "$skill_repo_root" ] && [ "$current_repo" = "$skill_repo_root" ]; then
echo "⚠ Currently in skill repository, switching to publish directory"
fi
cd "$publish_repo_root" || { echo "❌ Failed to cd to $publish_repo_root"; 1; }
! git rev-parse --git-dir > /dev/null 2>&1;
git init
git config user.name
git config user.email
Step 5: Create Pull Request
-
Build the PR title: Test Plan: <feature> (v<version>)
-
Resolve the strategy browse URL from the configured Jira URL (do not hardcode a Jira host; falls back to the JIRA_BASE_URL alias):
jira_url="${JIRA_URL:-$JIRA_BASE_URL}"
if [ -z "$jira_url" ]; then
echo "ERROR: JIRA_URL (or JIRA_BASE_URL) is not set — cannot build the strategy link." >&2
exit 1
fi
strat_url="${jira_url%/}/browse/${source_key}"
-
Build the PR body by reading Section 1 (Executive Summary) from TestPlan.md. Format it as:
## Test Plan: <feature>
**Strategy**: [<source_key>](<strat_url>)
**Version**: <version>
### Summary
<Section 1.1 Purpose content>
### Scope
<Section 1.2 Scope — in-scope items only, as bullet list>
### Test Objectives
<Section 1.3 >
TestPlan.md
TestPlanGaps.md (if exists)
test
Step 6: Confirm
-
Display the PR URL to the user
-
Show a summary:
If new PR created:
Published successfully
- PR: <PR_URL>
- Branch:
test-plan/<source_key>
- Version:
- Status: frontmatter updated to
In Review
- Reviewers: <list or "none assigned">
The test plan is now ready for review.
If existing PR updated:
PR updated successfully
- PR: <PR_URL>
- Branch:
test-plan/<source_key>
- Version: <old_version> → <new_version>
- New commits pushed
The PR has been updated with the latest changes.
-
Switch back to the previous branch:
git checkout -
What this skill does NOT do
- Does NOT generate or modify test plan content — use
/test-plan-create for that
- Does NOT apply review feedback — use
/test-plan-resolve-feedback for that
- Does NOT support Confluence or Google Drive — GitHub PRs only
- Does NOT auto-increment the
version field — version is set by /test-plan-create and only bumped by /test-plan-resolve-feedback after changes
$ARGUMENTS