agentleFS
grzmolgrzmol/vibe-force/skills/sf-post-deploy-verification/SKILL.md

sf-post-deploy-verification

Skill2 starsChanged 2 months agoMITView on GitHub

Post-deploy verification of Salesforce work in a real org - reading deploy results with sf project deploy report and sf project deploy resume, running org Apex tests with sf apex run test and sf apex get test, executing anonymous-Apex smoke probes with sf apex run --file, verifying data with sf data query, verifying configuration (permission set assignments, custom metadata rows, active flow versions, scheduled jobs via CronTrigger, platform event channels, Named Credential reachability), checking org health with sf org list limits and AsyncApexJob failures, and deciding between rollback and forward fix when verification fails. Use this skill in wave 4 after a deploy or quick deploy completes, when a deployment reports Succeeded but behaviour is unproven, when interpreting deploy or Apex test failures, when authoring smoke scripts under scripts/apex/, or when producing the verification report at .vibeforce/reports/smoke-*.json.

The file

Files
SKILL.md

Post-Deploy Verification

A deploy that says Succeeded proves that metadata compiled, not that the feature works. This skill is the wave-4 gate: deploy result -> org Apex tests -> smoke probes -> data and configuration checks -> org health -> report. Enforced by vf-check smoke and vf-check verify.

When to use

TriggerStart at
sf project deploy validate / quick finished or timed outDeployment result verification
Deploy status Succeeded but the story is unprovenWave-4 sequence
Deploy status Failed / SucceededPartialreferences/failure-triage.md
Apex tests failed in the org but pass locallyreferences/failure-triage.md, skill sf-apex-testing
Need a new smoke probereferences/smoke-apex-scripts.md
Need to confirm config landed (perm sets, flows, jobs, custom metadata)references/org-health-queries.md
Asked "is this safe to leave in the org?"Rollback vs forward-fix decision tree

Wave-4 sequence

Wave 3 (sf-deploy-engineer) runs serially: deploy-validate then deploy-quick. Wave 4 runs the two branches in parallel; both must pass before the story is done.

StepAgentCommandPass criteria
4.0sf-deploy-engineersf project deploy report --job-id <id> --target-org <alias> --jsonresult.status = Succeeded, numberComponentErrors = 0
4.1asf-test-engineervf-check apex --target-org <alias> --tests <list>All tests pass; class coverage >= gates.apexClassCoverageMin; org coverage >= gates.apexOrgCoverageMin
4.1bsf-org-verifiervf-check smoke --target-org <alias>Every probe exits 0; no new AsyncApexJob failures; limits within thresholds
4.2sf-org-verifierdata + configuration queries (references/org-health-queries.md)Row counts, active flow versions, perm-set assignments, scheduled jobs, custom metadata all as expected
4.3sf-org-verifierwrites .vibeforce/reports/smoke-<ISO>.jsonReport artefact present with status: "pass"
4.4sf-orchestratorreads both reportsFails the story if either branch failed; applies the rollback/forward-fix tree
node "${CLAUDE_PLUGIN_ROOT}/scripts/checks/vf-check.mjs" verify \
  --target-org vf-int --tests OrderServiceTest,BillingGatewayTest

verify = apex + smoke. Use all when the local gate has not run in this session.

1. Deployment result verification

# Status without polling (no --wait = single check)
sf project deploy report --job-id 0Afxx00000000AAA --target-org vf-int --json

# Poll until done, capture coverage and JUnit artefacts
sf project deploy report --use-most-recent --wait 30 \
  --coverage-formatters json-summary --junit --results-dir .vibeforce/reports/deploy \
  --target-org vf-int

# Resume watching a deploy that timed out or ran with --async (also updates source tracking)
sf project deploy resume --job-id 0Afxx00000000AAA --wait 30 --target-org vf-int
FactValue
Job id lifetime10 days from the start of the deploy operation
--use-most-recent lookback3 days
report vs resumereport does not update source tracking; resume does
--coverage-formatters valuesclover, cobertura, html-spa, html, json, json-summary, lcovonly, none, teamcity, text, text-summary
Commands that return a job iddeploy start, deploy validate, deploy quick, deploy cancel

Deploy status JSON (field names from @salesforce/source-deploy-retrieve MetadataApiDeployStatus):

{
  "status": 0,
  "result": {
    "id": "0Afxx00000000AAA",
    "status": "Succeeded",
    "success": true,
    "done": true,
    "checkOnly": false,
    "numberComponentsTotal": 42,
    "numberComponentsDeployed": 42,
    "numberComponentErrors": 0,
    "numberTestsTotal": 118,
    "numberTestsCompleted": 118,
    "numberTestErrors": 0,
    "runTestsEnabled": true,
    "rollbackOnError": true,
    "details": {
      "componentFailures": [],
      "componentSuccesses": [],
      "runTestResult": {
        "numFailures": "0",
        "numTestsRun": "118",
        "totalTime": "92345.0",
        "failures": [],
        "codeCoverage": [],
        "codeCoverageWarnings": []
      }
    }
  }
}

result.status values: Pending, InProgress, Succeeded, SucceededPartial, Failed, Canceling, Canceled, Finalizing, FinalizingFailed.

Component failure fields (details.componentFailures[]): fullName, componentType, fileName, problem, problemType (Error | Warning), lineNumber, columnNumber, success, created, changed, deleted. Test failure fields (details.runTestResult.failures[]): name, methodName, message, stackTrace, time, type, packageName.

Failure triage table for common problem values: references/failure-triage.md.

2. Org Apex tests after deploy

Validation already ran tests, but a quick deploy skips them, and post-deploy runs catch data-dependent failures that a validation on a different snapshot missed.

# Targeted, synchronous where possible (single class), JSON for the report
sf apex run test --tests OrderServiceTest.createsOrder --tests BillingGatewayTest \
  --code-coverage --result-format json --wait 20 --target-org vf-int

# Suite-based regression set
sf apex run test --suite-names Order_Regression --code-coverage --detailed-coverage \
  --result-format human --wait 30 --target-org vf-int

# Whole-org local tests (release gate)
sf apex run test --test-level RunLocalTests --code-coverage --result-format json \
  --wait 60 --output-dir .vibeforce/reports/apex --target-org vf-int

# Async run that timed out: fetch by test run id
sf apex get test --test-run-id 707xx0000000001 --code-coverage --result-format json --target-org vf-int
FlagMeaning
--testsClass or Class.method; mutually exclusive with --class-names and --suite-names
--class-names / --suite-namesWhole classes / suites; also mutually exclusive with the others
--test-levelRunSpecifiedTests, RunLocalTests (default), RunAllTestsInOrg
--synchronousSingle class only; otherwise runs async and returns a test run id
--waitStreaming client socket timeout in minutes
--code-coverage + --result-formatRequired together to see coverage; --detailed-coverage needs human format
--conciseFailures only (human format)
PermissionRequires the View All Data system permission

testRunCoverage in JSON/JUnit output is the percentage of covered lines across all Apex classes exercised by that run - it is not the org-wide coverage figure. RunSpecifiedTests requires each class and trigger in the deployment package to reach 75% individually. Gate values live in .vibeforce/config.json (apexOrgCoverageMin 85, apexClassCoverageMin 75).

3. Anonymous-Apex smoke probes

sf apex run --file scripts/apex/smoke-core.apex --target-org vf-int --json

sf apex run --json returns result.compiled, result.success, result.logs, and result.diagnostic[] (with compileProblem, exceptionMessage, exceptionStackTrace, line, column). A probe signals failure by throwing, which flips success to false.

Non-negotiable probe rules:

RuleWhy
Guard on Organization.IsSandbox before any DMLA smoke probe must never mutate production data
Prefer queries, Schema describes, and service instantiation over DMLRead-only probes are safe everywhere
When DML is unavoidable, wrap in Database.setSavepoint() + Database.rollback()Nothing persists, even in a sandbox
Emit VF_PROBE marker lines with LoggingLevel.ERRORSurvives default log filters; the runner greps them
Throw on failure, never System.debug and continueNon-zero exit code is the machine-readable signal
Callout probes point at sandbox endpoints onlySee skill sf-integration-patterns

Skeleton (full scripts in references/smoke-apex-scripts.md):

// scripts/apex/smoke-core.apex - read-only; safe in any org.
Organization org = [SELECT Id, Name, IsSandbox, OrganizationType, InstanceName FROM Organization LIMIT 1];
System.debug(LoggingLevel.ERROR, 'VF_PROBE org=' + org.Name + ' sandbox=' + org.IsSandbox);

List<String> failures = new List<String>();

// 1. Key objects and fields are visible and describable.
Map<String, Schema.SObjectType> globalDescribe = Schema.getGlobalDescribe();
for (String objectName : new List<String>{ 'Order__c', 'Order_Line__c' }) {
    if (!globalDescribe.containsKey(objectName.toLowerCase())) {
        failures.add('missing object: ' + objectName);
    }
}

// 2. Service classes instantiate and answer.
try {
    Integer open = OrderService.countOpenOrders();
    System.debug(LoggingLevel.ERROR, 'VF_PROBE openOrders=' + open);
} catch (Exception e) {
    failures.add('OrderService.countOpenOrders: ' + e.getMessage());
}

if (!failures.isEmpty()) {
    throw new IllegalArgumentException('VF_PROBE_FAIL ' + String.join(failures, ' | '));
}
System.debug(LoggingLevel.ERROR, 'VF_PROBE_OK smoke-core');

4. Data verification

# Row counts after a data-touching deploy
sf data query --target-org vf-int --result-format json \
  --query "SELECT COUNT(Id) total FROM Order__c WHERE Status__c = 'Open'"

# Referential integrity: orphans that a schema change may have created
sf data query --target-org vf-int \
  --query "SELECT COUNT(Id) FROM Order_Line__c WHERE Order__c = null"

# Picklist values and record types actually available
sf data query --target-org vf-int --use-tooling-api \
  --query "SELECT Id, DeveloperName, SobjectType, IsActive FROM RecordType WHERE SobjectType = 'Order__c'"

Full query catalogue (picklist values via PicklistValueInfo, custom metadata rows, duplicate external ids, storage): references/org-health-queries.md. Data loading and export mechanics: skill sf-data-management.

5. Configuration verification

# Permission set assignment landed on the integration user
sf data query --target-org vf-int --query \
  "SELECT PermissionSet.Name, Assignee.Username FROM PermissionSetAssignment WHERE PermissionSet.Name = 'Order_Management'"

# Flow active version
sf data query --target-org vf-int --query \
  "SELECT ApiName, Label, IsActive, ProcessType, TriggerType, ActiveVersionId FROM FlowDefinitionView WHERE ApiName = 'Order_Routing'"

# Scheduled jobs present and due to fire
sf data query --target-org vf-int --query \
  "SELECT CronJobDetail.Name, State, NextFireTime, PreviousFireTime, TimesTriggered FROM CronTrigger ORDER BY NextFireTime"

# Custom metadata rows deployed
sf data query --target-org vf-int --query \
  "SELECT DeveloperName, Label FROM Integration_Setting__mdt ORDER BY DeveloperName"

# Platform event channel / named credential presence
sf org list metadata --metadata-type PlatformEventChannel --target-org vf-int --json
sf org list metadata --metadata-type NamedCredential --target-org vf-int --json

Named Credential reachability is a callout, so it belongs in a smoke probe, not a query - see references/smoke-apex-scripts.md and skill sf-integration-patterns.

6. Limits and org health

sf org list limits --target-org vf-int --json          # alias: sf limits api display
sf org display --target-org vf-int --json              # connection, instance URL, org id
sf org display --target-org vf-int --verbose           # includes the SFDX auth URL - never log it

# Async failures created by the deploy or the smoke run
sf data query --target-org vf-int --query \
  "SELECT ApexClass.Name, JobType, Status, ExtendedStatus, NumberOfErrors, CompletedDate \
   FROM AsyncApexJob WHERE Status IN ('Failed','Aborted') AND CreatedDate = TODAY ORDER BY CompletedDate DESC"

# Unexpected exceptions in logs produced during the probe window
sf apex list log --target-org vf-int --json
sf apex get log --log-id 07Lxx00000000AA --target-org vf-int

Limits worth gating on: DailyApiRequests, DailyAsyncApexExecutions, DailyBulkApiBatches, DataStorageMB, FileStorageMB, HourlyPublishedPlatformEvents, DailyDeliveredPlatformEvents, HourlyODataCallout, ActiveScratchOrgs. Label descriptions: https://developer.salesforce.com/docs/platform/api-rest/guide/resources-limits.html

7. UI-level verification

OptionCommand / toolUse
Manual spot checksf org open --path lightning/o/Order__c/list --target-org vf-intHuman confirmation of a page or layout
Generate a link without opening a browsersf org open --path lightning --url-only --target-org vf-intPaste into a ticket or report
Open the component in its buildersf org open --source-file force-app/main/default/flows/Order_Routing.flow-meta.xml --target-org vf-intConfirm a Flow/FlexiPage deployed as intended
Automated UI regressionPlaywright, or UTAM page objects for LightningOut of scope for vf-check; run in a dedicated suite
Local component previewsf lightning dev app / sf lightning dev componentPre-deploy only; it does not verify the deployed org state (skill sf-local-development)

LWC behaviour is proven by Jest locally (vf-check jest) plus one manual or Playwright pass; do not pretend an org query verifies a rendered component.

8. Rollback vs forward fix

flowchart TD
    A[Verification failed] --> B{Deploy status}
    B -->|Failed, nothing applied| C[Fix source, re-validate, quick deploy]
    B -->|Succeeded / SucceededPartial| D{Blast radius}
    D -->|Data corruption or security exposure| E[Roll back now: deploy previous release, then repair data]
    D -->|Feature broken, contained| F{Fix available within one deploy cycle?}
    F -->|Yes| G[Forward fix: patch, validate, quick deploy]
    F -->|No| H{Can the feature be switched off?}
    H -->|Yes| I[Disable: deactivate flow / flip custom metadata flag / unassign permission set]
    H -->|No| E
    E --> J[Post-incident: add the missing check to vf-check smoke]
    G --> J
    I --> J
SituationActionCommand
Deploy failed, nothing appliedForward fix; the org is untouchedvf-check deploy-validate after the code change
Apex test failure caused by test data onlyForward fix the testsf apex run test --tests ...
Feature broken but isolatedForward fixsf project deploy validate then sf project deploy quick
Flow misrouting recordsDisable firstdeactivate the flow version, then forward fix
Permission exposureRoll back the permission set immediatelydeploy the previous permission set version; revoke assignments
Data already corruptedRoll back code, then repair dataprevious release deploy + corrective data job
Destructive change deployed by mistakeRe-deploy the removed metadata from source controlsf project deploy start --source-dir <path>

Salesforce has no transactional "undo" for a completed deploy: rollback means deploying the previous known-good source. Detail and command sequences: references/failure-triage.md; deploy strategy and release branching: skill sf-deployment-strategies.

9. Report artefact

vf-check smoke writes <project>/.vibeforce/reports/smoke-<ISO>.json:

{
  "check": "smoke",
  "startedAt": "2026-09-12T09:14:22.104Z",
  "durationMs": 48210,
  "status": "fail",
  "targetOrg": "vf-int",
  "orgInfo": { "orgId": "00Dxx0000001gPFEAY", "instanceUrl": "https://example.my.salesforce.com", "isSandbox": true },
  "deploy": { "jobId": "0Afxx00000000AAA", "status": "Succeeded", "numberComponentErrors": 0 },
  "gates": { "asyncApexFailures": 0, "apiUsagePercentMax": 80 },
  "findings": [
    {
      "id": "smoke.apex.smoke-integration",
      "severity": "error",
      "probe": "scripts/apex/smoke-integration.apex",
      "message": "VF_PROBE_FAIL Billing_API health check returned 503",
      "evidence": { "compiled": true, "success": false, "line": 18 }
    }
  ],
  "raw": { "probes": [], "limits": {}, "queries": {} }
}

status is pass when findings has no error entries. Exit codes follow the runner contract: 0 pass, 1 gate failed, 2 misconfiguration, 3 org/network error.

10. Checklist by change type

Change typeRequired checks
Apex onlydeploy report clean; sf apex run test --tests <changed tests> --code-coverage; smoke-core.apex; AsyncApexJob failures = 0
LWC onlyvf-check jest locally; deploy report clean; sf org open --path on the host page or a Playwright pass; browser console free of errors
Schema change (object, field, record type)deploy report clean; Schema describe probe; RecordType/PicklistValueInfo queries; orphan-row query; DataStorageMB limit
Permission changePermissionSetAssignment query; smoke-permissions.apex (describe-based CRUD/FLS assertions); negative check that a non-assigned user is still denied
Integration changeNamed Credential/External Credential presence; smoke-integration.apex callout probe against a sandbox endpoint; dead-letter count = 0; HourlyPublishedPlatformEvents and DailyDeliveredPlatformEvents within limits
Flow changeFlowDefinitionView active version matches the deployed version; flow test run if flow tests exist; probe the records the flow should have produced
Scheduled job changeCronTrigger row exists with the expected CronJobDetail.Name and a future NextFireTime; AsyncApexJob for the previous run is Completed
Data loadrow counts; duplicate external-id query; DailyBulkApiBatches usage; failed-record results from the bulk job

Exact commands and pass criteria per row: references/verification-catalogue.md.

References

  • references/verification-catalogue.md - change type -> checks -> exact commands -> pass criteria.
  • references/smoke-apex-scripts.md - complete runnable anonymous-Apex probes and how to parse their output.
  • references/org-health-queries.md - SOQL and Tooling API queries for jobs, flows, permissions, limits, and errors.
  • references/failure-triage.md - deploy/test/smoke failure -> diagnosis -> action, including rollback versus forward fix.

Official documentation used:

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.