From claude-sf-toolkit
Commit SF metadata changes to a DevOps Center work item branch — primary mode for uncommitted changes, recovery mode for cherry-picks
How this command is triggered — by the user, by Claude, or both
Slash command
/claude-sf-toolkit:devops-commitdevops/The summary Claude sees in its command listing — used to decide when to auto-load this command
# /devops-commit — DevOps Center Work Item Commit
Commit SF metadata changes directly to a DevOps Center work item branch. SF metadata (`{context.metadataPath}/../..` tree) must NEVER be committed to main — it belongs exclusively on WI branches, which DevOps Center promotes through the pipeline and merges back to main after promotion.
**Primary mode:** Stage and commit uncommitted metadata changes directly to the WI branch.
**Recovery mode:** Cherry-pick a commit that was accidentally made on main onto the WI branch.
**Arguments:** `$ARGUMENTS`
Arguments can be:
- `WI-NNNNNN` — work it...Commit SF metadata changes directly to a DevOps Center work item branch. SF metadata ({context.metadataPath}/../.. tree) must NEVER be committed to main — it belongs exclusively on WI branches, which DevOps Center promotes through the pipeline and merges back to main after promotion.
Primary mode: Stage and commit uncommitted metadata changes directly to the WI branch. Recovery mode: Cherry-pick a commit that was accidentally made on main onto the WI branch.
Arguments: $ARGUMENTS
Arguments can be:
WI-NNNNNN — work item number (required). Commits uncommitted metadata changes to the WI branch.WI-NNNNNN --cherry-pick abc1234 — recovery mode: cherry-pick an existing commit onto the WI branchWI-NNNNNN --cherry-pick abc1234 def5678 — recovery: multiple commit SHAs (cherry-picked in order)WI-NNNNNN --cherry-pick HEAD~3..HEAD — recovery: commit range--target-org {alias} — override the deploy target org. Defaults to the user's SF CLI default org (sf config get target-org). Only affects the Step 7 deploy; WI queries always target {context.orgs.productionAlias}.--skip-validation — bypass the /validate-build gate before promotion (DOC mode) or PR creation guidance (GHA mode)Cache-first resolution:
.claude/sf-toolkit-cache.json in the project root._cache.expiresAt is after the current date/time, and no --target-org override was provided:
.sf/config.json — confirm target-org matches orgs.devAlias in the cached context._cache). Skip the agent dispatch.sf-toolkit-resolve agent. It will resolve fresh context and update the cache.Use the returned context for all org references, team lookups, and path resolution in subsequent steps. If missing contains values this skill requires, stop and instruct the developer to run /setup.
If the resolved context contains workTracking.disabledSkills and it includes "devops-commit":
Not available in GitHub Actions mode.
In GitHub Actions projects, metadata commits go through the standard git workflow:
git add <files> git commit -m "feat: description (Fixes #NN)" git pushBefore opening a PR, run
/validate-buildto verify the build against the design spec:/validate-build #{NN}Then create the PR:
gh pr create --title "feat: description" --body "Fixes #NN"GitHub Actions handles CI validation on PR open and deployment on merge to main.
Stop here — do not proceed to the workflow steps below.
Run the git and org suites from /skill-preflight logic inline (do NOT invoke the skill — just run the same checks directly to avoid circular dependencies):
git status. If there are staged but uncommitted changes, stop — report the staged files and ask the user to commit or stash first.--cherry-pick mode).WI-* branch, note it — no branch switch needed.{context.orgs.productionAlias} (needed to query work items):
sf data query --query "SELECT Id FROM Organization LIMIT 1" --target-org {context.orgs.productionAlias} --json
If unreachable, warn that work item validation will be skipped but the commit can still proceed.If any stop-level issue is found, report it and exit. For warn-level issues, report and ask whether to proceed.
Show the user context to help them decide:
Uncommitted metadata changes:
git status --short -- '{context.metadataPath}/../..'
Open work items in DevOps Center (if {context.orgs.productionAlias} is connected):
Read SF_USER_ID from .env at repo root. If present, filter to the current user's assigned WIs:
SELECT Name, sf_devops__Subject__c, sf_devops__State__c, sf_devops__Assigned_To__r.Name, CreatedDate
FROM sf_devops__Work_Item__c
WHERE sf_devops__Project__c = '{context.devopsCenter.projectId}'
AND sf_devops__Promoted__c = false
AND sf_devops__Assigned_To__c = '{context.user.sfUserId}'
ORDER BY CreatedDate DESC
LIMIT 15
If SF_USER_ID is not set in .env, omit the sf_devops__Assigned_To__c filter to show all open WIs.
Ask the user: "Which work item should these changes go to?"
Parse the WI number and optional --cherry-pick flag with commit reference(s).
Validate the WI number format: Must match WI-\d{6} (e.g., WI-000054).
If --cherry-pick mode: Validate commit SHAs — for each SHA, verify it exists:
git cat-file -t {sha}
If a range is specified (e.g., HEAD~3..HEAD), expand it to individual SHAs:
git rev-list --reverse {range}
If {context.orgs.productionAlias} is connected, verify the work item exists and is in a valid state:
SELECT Id, Name, sf_devops__Subject__c, sf_devops__Status__c
FROM sf_devops__Work_Item__c
WHERE Name = '{WI-NNNNNN}'
LIMIT 1
Completed: warn — committing against a completed work item is unusual. Ask to confirm.The branch must have been created by DevOps Center (via "Check Out" in the UI). Check if it exists:
git fetch origin
git branch -r --list "origin/{WI-NNNNNN}"
If the branch exists: proceed
If the branch does NOT exist: stop — display this message:
Branch origin/{WI-NNNNNN} not found.
DevOps Center must create this branch — creating it manually in git
won't create the required sf_devops__Branch__c record.
Action required:
1. Open DevOps Center in {context.orgs.productionAlias}
2. Find work item {WI-NNNNNN}
3. Click "Check Out" to create the branch
4. Re-run: /devops-commit {WI-NNNNNN} {commit refs}
Before executing, show the user exactly what will happen:
## Commit Plan
Work item: {WI-NNNNNN} — {subject from Step 2, or "unverified" if org unavailable}
Target branch: {WI-NNNNNN}
Return to: {current branch}
Files to commit ({n}):
{list of metadata files from git status}
Commit message: [docs-only] WI-{number}: {description}
(or non-docs-only message if changes include logic)
--cherry-pick):## Cherry-Pick Plan (recovery)
Work item: {WI-NNNNNN} — {subject from Step 2, or "unverified" if org unavailable}
Target branch: {WI-NNNNNN}
Return to: {current branch}
Commits to cherry-pick ({n}):
{sha1} {commit message}
{sha2} {commit message}
Files affected:
{list of files from git diff --name-only for the commit(s)}
Ask: "Proceed?" Wait for confirmation.
Run the following sequence. If any step fails, abort and report — do NOT continue with partial state.
git stash push -m "devops-commit: auto-stash before switching to {WI-NNNNNN}"
Record that a stash was created (we'll pop it in Step 6).
git checkout {WI-NNNNNN}
If this fails because the branch only exists on remote:
git checkout -b {WI-NNNNNN} origin/{WI-NNNNNN}
Primary mode: Restore only the metadata files from the stash onto the WI branch, stage, and commit:
git checkout stash@{0} -- {context.metadataPath}/path/to/file1 {context.metadataPath}/path/to/file2 ...
git add {context.metadataPath}/
git commit -m "{commit message}"
IMPORTANT: Only restore metadata files from the stash. Do NOT restore docs, skills, or other repo-only files — those belong on main.
Recovery mode (--cherry-pick): Cherry-pick the specified commit(s):
git cherry-pick {sha1} {sha2} ...
If cherry-pick conflicts:
git cherry-pick --abort -> return to original branch -> report failuregit cherry-pick --continueAfter staging the metadata files (Step 5C) and before committing, check whether any .flow-meta.xml files are in the staged changes:
git diff --cached --name-only -- '{context.metadataPath}/flows/*.flow-meta.xml'
If flow XML files are found:
Extract flow names from the staged file paths (strip directory and .flow-meta.xml suffix).
Run the description sync script (local project script first, plugin template fallback):
if [ -f scripts/flow-description-sync.js ]; then
node scripts/flow-description-sync.js {FlowName1} {FlowName2} ...
fi
If neither the local scripts/flow-description-sync.js nor ${CLAUDE_PLUGIN_ROOT}/script-templates/flow-description-sync.js exists, skip this sub-step silently — flow description sync is optional.
The script:
docs/flows/ directories"{purpose sentence} Docs: {github url}"<description> element in the XML (255 char max)Re-stage the updated XMLs (the script modifies files in-place):
git add {context.metadataPath}/flows/{FlowName}.flow-meta.xml ...
Report what was updated in the commit preview:
Flow XML descriptions auto-synced ({n} flows):
- {FlowName} — "{first 60 chars of description}..."
Skipped (no doc found): {list, if any}
Important constraints:
<description> element — never modifies flow logic, triggers, or variablesdocs/flows//doc-flows run)main are NOT modified — they continue to reflect the production-current state.pending-docs.txt for the next /doc-flows run to update the markdown docsWhy this works safely: The description is metadata-only — it doesn't affect flow execution. Including it in the same WI commit means the description deploys to production alongside the logic change, eliminating the need for a separate description-only deployment.
git push origin {WI-NNNNNN}
If push fails (e.g., remote rejected), report the error. Do NOT force-push.
git checkout {original branch from Step 0}
git stash pop
If the stash pop conflicts, warn the user — their working changes may need manual resolution.
After stash pop: The metadata files that were committed to the WI branch will show as "already up to date" in the working directory (since stash had them modified and now the working directory matches). The non-metadata files (docs, etc.) will be restored as unstaged changes on main — where they belong.
After the WI branch is pushed, the metadata must also be deployed to the target org for DevOps Center to pick it up for promotion. Resolve the target org:
--target-org {alias} was provided, use that.sf config get target-org --jsonsf project deploy start --target-org {target-org} --source-dir {file1} --source-dir {file2} ...
Production safety gate (Tier 2): If {target-org} is {context.orgs.productionAlias} or contains "prod"/"production", display a prominent warning and ask for explicit confirmation before deploying.
Confirm with the user before deploying. If the deploy fails on any file, report the failure and offer to retry the failing files individually.
Apply superpowers:verification-before-completion discipline: no success claims without fresh evidence.
After the deploy command returns, run verification to confirm the metadata actually landed in the target org. Do NOT report success based solely on the deploy command's exit code — deploy commands can report success without actually deploying.
Run sf project deploy report to confirm the deployment:
sf project deploy report --use-most-recent --target-org {target-org} --json
Spot-check at least one deployed component in the org. Choose the verification method based on what was deployed:
| Metadata type | Verification command |
|---|---|
| Custom fields | sf sobject describe --sobject {ObjectName} --target-org {target-org} — confirm the field appears |
| Flows | sf data query --query "SELECT Id, Status FROM Flow WHERE Definition.DeveloperName = '{FlowName}' AND Status = 'Active'" --target-org {target-org} |
| Permission sets | sf data query --query "SELECT Id FROM PermissionSet WHERE Name = '{PermSetName}'" --target-org {target-org} |
| Other types | Use the most appropriate describe/query for that type |
Evidence gate: Only proceed to Step 8 if verification confirms the deploy landed. If verification fails or contradicts the deploy command's reported success:
--metadata flag instead of --source-dir, or deploy individual files## /devops-commit Complete
Work item: {WI-NNNNNN} — {subject}
Mode: {Primary commit / Cherry-pick recovery}
Files committed: {n}
Branch {WI-NNNNNN} pushed to origin.
Deployed to {target-org}: {Yes / No / Partial}
Current branch: {original branch}
Before offering promotion, check whether /validate-build has been completed for this work item:
.last-validate-build in the project root. If it exists, read the line — format is {date}|{WI-or-issue}|{summary}. Check that the WI/issue reference matches the current work item and the date is today (stale validations from prior days should re-run).If validation has NOT been completed:
**Validation recommended before promotion.**
/validate-build has not been run for {WI-NNNNNN}.
Options:
1. **Run validation now** — invoke `/validate-build {WI-NNNNNN}` before promoting
2. **Skip validation** — proceed to promotion without validation (--skip-validation)
3. **Skip promotion** — promote manually later after validation
If the user chooses option 1, invoke /validate-build with the WI number. After it completes, return to the promotion offer below.
If the user chooses option 2 (or if the original /devops-commit invocation included --skip-validation), proceed directly to promotion.
If validation IS complete (or skipped):
After a successful commit + push + deploy, offer to start the promotion pipeline via MCP instead of requiring the user to open the DevOps Center UI:
Ready to promote? Options:
1. **Check for conflicts first:** detect merge conflicts before promoting
2. **Promote now:** create a pull request and promote through the pipeline
3. **Skip:** promote manually in DevOps Center later
If the user chooses option 1:
detect_devops_center_merge_conflict(workItemName: "{WI-NNNNNN}")
If conflicts are detected, report them and ask whether to attempt resolution or abort. If clean:
promote_devops_center_work_item(workItemName: "{WI-NNNNNN}")
If the user chooses option 2 (promote directly):
promote_devops_center_work_item(workItemName: "{WI-NNNNNN}")
Important: Promotion targets Staging, not Production. The Staging → Production promotion is always human-initiated in DevOps Center (bundled promotions).
If the user chooses option 3, show manual next steps:
Next steps:
1. Open DevOps Center in {context.orgs.productionAlias}
2. Work item {WI-NNNNNN} should show the new commit(s)
3. Promote through the pipeline when ready
If the skill fails mid-execution, always attempt to return to a clean state:
git cherry-pick --abortgit checkout {original branch}git stash popNever leave the user on the WI branch or with an uncommitted cherry-pick.
npx claudepluginhub chriscamptn/claude-sf-toolkit --plugin claude-sf-toolkit