Dylan AndersenDylan Andersen's Docs
Cursor + SalesforceExpert Cursor

Demo Reset & Repeatability

The scripted reset every SE wishes they had the morning of a discovery call. Clear records, re-seed data, re-assign perm sets, re-activate the agent.

Every SE has lived the 8:45 AM version of this: demo at 9, data from yesterday's call still in the org, test user missing a permission, agent in a weird state, no script to fix it. This page is the version where you type one command and go get coffee while the demo resets itself.

Demo Reset and Repeatability hero

The target

By the end of this page, you should be able to run npm run demo:reset (or ./scripts/demo-reset.sh) from inside your POC project, walk away for two minutes, and come back to an org in a clean, scripted-from-scratch demo state.

What a reset actually means

A "reset" is a specific sequence of operations against a specific org. Spell them out, then script them.

A realistic demo reset for a Customer Service Agentforce POC:

  1. Target the demo org (sf config set target-org cs-demo --global=false).
  2. Delete the records created during the previous demo (Cases, Customer Insights, test Contacts).
  3. Re-seed the canonical demo dataset.
  4. Reset the test user's permission set group (unassign, reassign, so it re-evaluates).
  5. Restart any active Agentforce agent (deactivate, activate).
  6. Run a smoke check: one agent query, one record view, one flow.

Everything else is a variation of these six steps.

The scripts folder

Put reset logic in scripts/demo-reset/ so it's committed and reproducible. A common layout:

scripts/
└── demo-reset/
    ├── reset.sh
    ├── 01-clear.apex
    ├── 02-seed.apex
    ├── 03-perms.sh
    ├── 04-agent.sh
    └── 05-smoke.apex

One file per step. Each step runs cleanly even if the previous one failed partway. If step 3 blows up, you should be able to rerun the whole script from the top without duplicating records or double-assigning permissions.

Writing each step

Step 1: clear records

Anonymous Apex is the right tool here. Keep deletes scoped to the demo user's records where possible.

// 01-clear.apex
List<Case> oldCases = [
    SELECT Id FROM Case
    WHERE CreatedDate = LAST_N_DAYS:7
    AND Origin = 'Demo'
];
delete oldCases;

List<CustomerInsight__c> oldInsights = [
    SELECT Id FROM CustomerInsight__c WHERE IsDemo__c = true
];
delete oldInsights;

System.debug('Cleared ' + oldCases.size() + ' cases, ' + oldInsights.size() + ' insights.');

Run it:

sf apex run --file scripts/demo-reset/01-clear.apex --target-org cs-demo

Step 2: seed

For small seed datasets, use Apex. For larger ones, use sf data import tree against a seed JSON file.

# Small seed
sf apex run --file scripts/demo-reset/02-seed.apex --target-org cs-demo

# Larger tree
sf data import tree \
  --files scripts/demo-reset/seed-accounts.json,scripts/demo-reset/seed-contacts.json \
  --target-org cs-demo

The sf-data skill can generate both the Apex seed and the tree JSON from a record shape you describe.

Step 3: permissions

Unassign and reassign to force a clean state:

# 03-perms.sh
DEMO_USER_ID="005XX000001abcd"
GROUP="CS_Agent_Demo_User"

sf org unassign permsetgroup --name "$GROUP" --on-behalf-of "$DEMO_USER_ID" --target-org cs-demo || true
sf org assign permsetgroup --name "$GROUP" --on-behalf-of "$DEMO_USER_ID" --target-org cs-demo

The || true prevents a "not currently assigned" error from stopping the script.

Step 4: restart the agent

Agentforce Builder agents are metadata. Deactivating and reactivating forces a clean session.

# 04-agent.sh
sf org assign permset --name AgentforceUser --target-org cs-demo
sf data update record --sobject BotVersion --record-id "1iFXX..." --values "Status='Inactive'" --target-org cs-demo
sf data update record --sobject BotVersion --record-id "1iFXX..." --values "Status='Active'" --target-org cs-demo

(Or: if the agent is Agent Script, sf agent publish authoring-bundle re-publishes from the source of truth.)

Step 5: smoke check

A tiny check that proves the reset worked.

// 05-smoke.apex
List<Case> sampleCases = [SELECT Id, Subject FROM Case WHERE Origin = 'Demo' LIMIT 5];
System.assertEquals(3, sampleCases.size(), 'Expected 3 seed cases after reset.');
System.debug('Demo org is ready.');

Run it. If the assert throws, the reset didn't complete and you'd rather know at 8:47 than during the demo at 9:01.

The wrapping reset.sh

Tie them together:

#!/usr/bin/env bash
set -euo pipefail

ORG="${1:-cs-demo}"
echo "Resetting $ORG..."

sf apex run --file scripts/demo-reset/01-clear.apex --target-org "$ORG"
sf apex run --file scripts/demo-reset/02-seed.apex --target-org "$ORG"
./scripts/demo-reset/03-perms.sh
./scripts/demo-reset/04-agent.sh
sf apex run --file scripts/demo-reset/05-smoke.apex --target-org "$ORG"

echo "Demo org $ORG is ready."

Then expose it via package.json scripts:

{
  "scripts": {
    "demo:reset": "./scripts/demo-reset/reset.sh cs-demo",
    "demo:reset:uat": "./scripts/demo-reset/reset.sh cs-uat"
  }
}

Now it's npm run demo:reset and done.

Letting Cursor write the reset for you

You shouldn't have to hand-write all this. Ask the agent:

Scaffold a demo reset under scripts/demo-reset/ for the cs-demo org. The
demo involves the CustomerInsight__c object and a Case origin of 'Demo'.
The test user is demo@cs-poc.com. The permission set group is
CS_Agent_Demo_User. The Agentforce agent to restart is the CS Triage agent.

Use anonymous Apex for clear and seed, shell scripts for permissions and
agent restart, and a smoke Apex at the end. Produce all five files plus a
wrapping reset.sh and a package.json scripts entry.

Agent mode will generate the whole folder. Spot-check it, run it once against a scratch org, commit.

Repeatability patterns

One-command rebuild from zero

Sometimes the best reset is "throw the org away." If your demo fits in a scratch org:

sf org delete scratch --target-org cs-demo --no-prompt
sf org create scratch --definition-file config/project-scratch-def.json --alias cs-demo
sf project deploy start --target-org cs-demo
./scripts/demo-reset/reset.sh cs-demo

That's a 3–6 minute rebuild depending on feature flags. Tuck it behind npm run demo:rebuild and you have a guaranteed-clean org in one command.

Data snapshots

If the seed data takes minutes to build, export a snapshot once and import it on every reset:

sf data export tree \
  --query "SELECT Id, Name, Industry FROM Account WHERE Type='Demo'" \
  --output-dir scripts/demo-reset/snapshots/ \
  --target-org cs-demo

Then sf data import tree the files in step 2. Faster than rebuilding from Apex.

Golden org pattern

For large demos, maintain a "golden" scratch org that you never touch. When you need a fresh demo, clone its snapshot:

sf org create snapshot --source-org cs-golden --name cs-golden-snap
sf org create scratch --snapshot cs-golden-snap --alias cs-demo

Org Snapshots work for scratch orgs where the feature is enabled. Check availability before you plan around it.

Common traps

The reset "works" but the demo still breaks

You missed a step in the human version of the demo. Script the entire demo-day checklist: restart any external systems you call, clear browser cache for the Lightning session, make sure the demo user's password hasn't expired, check the agent version ID hasn't drifted.

Reset deletes too much

Scope every delete tightly. Origin = 'Demo' on Case, IsDemo__c = true on custom objects, CreatedDate = LAST_N_DAYS:N. Never run an unscoped delete in a shared org.

Reset reassigns permissions but the user still can't see a tab

Permission recalculation in Salesforce takes a few seconds after assignment. Put a sleep 5 before the smoke check, or have the smoke check retry.

Reset works locally but breaks on a teammate's machine

The IDs in your scripts (BotVersion ID, user ID) are org-specific. Move them into environment variables or have step 0 SOQL them out at runtime.

DEMO_USER_ID=$(sf data query --query "SELECT Id FROM User WHERE Username='demo@cs-poc.com'" --target-org cs-demo --json | jq -r '.result.records[0].Id')

Verification

Run the reset from scratch against a test scratch org you don't care about. If the smoke check passes, the reset works. Run it a second time (without any manual changes in between). It should still pass. A reset that only works on a clean org isn't a reset.

Next moves

  • Hooks & Automation if you want the reset to kick off automatically (pre-demo, nightly, etc.).
  • POCs & Hand-off Docs so the customer team can run your reset too.
  • Install sf-data if you haven't. It's the skill that writes the seed and clear Apex cleanly.

On this page