afv-library/skills/customizing-pre-work-brief/SKILL.md
Nitasha Walia cc7c475b01 feat: add Pre-Work Brief skills (base + customization)
Adds two complementary skills for setting up and customizing the
Field Service Mobile Pre-Work Brief feature.

skills/setting-up-pre-work-brief/

End-to-end setup of Pre-Work Brief on a Salesforce org with the
Einstein for Field Service add-on. Detects provisioning state, enables
Lightning Data Service, deploys the prompt template, assigns permission
sets, adds the PreWorkBriefPromptTemplate field to the Work Order
layout, and creates a fresh test Work Order + Service Appointment so
the admin can validate on-device immediately. Verified end-to-end
against an Einstein for Field Service trial org, May 2026.

skills/customizing-pre-work-brief/

Generator-pattern skill for vertical-specific Pre-Work Briefs deployable
as code. The admin provides industry, website, and a sentence about
what their technicians do; the skill produces custom objects, an
autolaunched PromptFlow, a sibling prompt template, a permission set,
and a test Work Order. Loads vertical-specific seed templates from
templates/<industry>.md.

Five seeds ship:
  - hvac.md (verified end-to-end on a trial org, May 2026)
  - banking.md, telecom.md, healthcare.md, retail-merchandising.md
    (scaffolds; ready for the next admin in those verticals to verify)

Each seed contributes recommended custom objects, a section structure
for the prompt template, vertical-specific rules, and a cadence example.
For verticals not in the library, --industry other synthesizes a draft
seed at runtime and writes it back to templates/ for next time.

The two skills compose: base setup gets PWB working at all on the
managed flow; customizing builds a parallel customer-specific bundle.
2026-05-20 09:53:01 -07:00

540 lines
27 KiB
Markdown

---
name: customizing-pre-work-brief
description: "Generate a vertical-specific Pre-Work Brief on Field Service Mobile, deployable as code. The admin provides industry, website, and a sentence describing what their technicians do; this skill produces custom objects, an autolaunched flow, a prompt template, a permission set, and a test record. TRIGGER when: user wants Pre-Work Brief tailored to their company or industry; user wants the brief to reference custom objects or fields specific to their business; user mentions website-grounded or vertical-specific briefs; user wants a second 'customized' brief alongside the default; user asks to clone or extend the managed Field Service flow; user references a specific vertical (HVAC, banking, telecom, healthcare, retail merchandising). DO NOT TRIGGER when: org has not yet completed base setup (use setting-up-pre-work-brief first); user wants Voice to Form; user wants Post-Work Summary; user wants a brand-new prompt template type from scratch (use Prompt Builder docs)."
allowed-tools: Bash Read Write Edit Glob Grep WebFetch
license: Apache-2.0
metadata:
version: "0.2.0"
last_updated: "2026-05-20"
argument-hint: "<org-alias> --company <name> --industry <hvac|banking|telecom|healthcare|retail-merchandising|other> --website <url> [--description <text>] [--service-resource <id>]"
compatibility: claude-code
---
# Customizing Pre-Work Brief
Generate a vertical-specific Pre-Work Brief that grounds on the customer's business and the data model their technicians actually work with. The skill produces a deployable bundle — custom objects, autolaunched flow, prompt template, permission set, plus a test Work Order — so an admin goes from "the default brief works but feels generic" to "the brief reads like someone who knows our business wrote it" in one run.
This skill is the second half of a two-step adoption journey. Step one — `setting-up-pre-work-brief` — gets PWB working at all on the managed flow. Step two — this skill — replaces the managed flow with a customer-specific bundle deployable entirely from metadata. No Save-As click in Flow Builder is required; the only manual step is one click to activate the prompt template, consistent with the base setup pattern.
The skill is **vertical-agnostic by design.** It ships with seed templates for common Field Service archetypes (HVAC, banking, telecom, healthcare, retail merchandising) at `templates/<industry>.md`. The seed contributes a section structure, recommended custom objects, and grounding patterns. For verticals not in the library, the skill synthesizes from the website + admin description and adds the result back into `templates/` for next time.
---
## Prerequisites
The base setup must be complete. Run `setting-up-pre-work-brief` against the same org first if any of the checks below fail:
```bash
ORG_ALIAS="${1:-}"
[ -z "$ORG_ALIAS" ] && { echo "Usage: customizing-pre-work-brief <org-alias> --company <name> --industry <vertical> --website <url>"; exit 1; }
echo "Checking base setup is in place..."
sf org list metadata --metadata-type GenAiPromptTemplate --target-org "$ORG_ALIAS" --json | \
jq -r '[.result[]? | select(.fullName == "Pre_Work_Brief")] | length as $n |
if $n > 0 then "✓ Default Pre_Work_Brief template deployed" else "✗ Default template missing — run setting-up-pre-work-brief first" end'
sf data query --target-org "$ORG_ALIAS" \
--query "SELECT DeveloperName FROM PermissionSetLicense WHERE DeveloperName = 'EinsteinFieldServicePsl' AND TotalLicenses > 0" --json | \
jq -r '.result.totalSize as $n | if $n > 0 then "✓ Einstein for Field Service PSL present" else "✗ PSL missing — run setting-up-pre-work-brief first" end'
sf sobject describe --sobject WorkOrder --target-org "$ORG_ALIAS" --json | \
jq -r '[.result.fields[]? | select(.name == "PreWorkBriefPromptTemplate")] | length as $n |
if $n > 0 then "✓ PreWorkBriefPromptTemplate field present" else "✗ Field missing — run setting-up-pre-work-brief first" end'
```
All three must report `✓` before continuing.
---
## Args
| Arg | Required | Default | Purpose |
|---|---|---|---|
| `<org-alias>` | yes | — | Target org. |
| `--company <name>` | yes | — | Company name. Used in the prompt template label and in business context generation. |
| `--industry <vertical>` | yes | — | One of: `hvac`, `banking`, `telecom`, `healthcare`, `retail-merchandising`, or `other`. Picks the seed template at `templates/<industry>.md`. If `other`, the skill synthesizes a template at runtime. |
| `--website <url>` | yes | — | Public website URL. The skill fetches the homepage to derive technician context. |
| `--description <text>` | no | (admin prompted) | 1-2 sentence description of what the company's technicians actually do on-site. If omitted, the skill asks. |
| `--service-resource <id>` | no | (admin prompted) | ServiceResource Id to assign the test Service Appointment to. |
| `--customized-template-name <name>` | no | `PreWorkBrief_<Industry>_<Company>` | Developer name of the new prompt template. Increment if running multiple times for the same org. |
Parse args:
```bash
COMPANY=""
INDUSTRY=""
WEBSITE=""
DESCRIPTION=""
SERVICE_RESOURCE_ID=""
TEMPLATE_NAME=""
while [ $# -gt 0 ]; do
case "$1" in
--company) COMPANY="$2"; shift 2 ;;
--industry) INDUSTRY="$2"; shift 2 ;;
--website) WEBSITE="$2"; shift 2 ;;
--description) DESCRIPTION="$2"; shift 2 ;;
--service-resource) SERVICE_RESOURCE_ID="$2"; shift 2 ;;
--customized-template-name) TEMPLATE_NAME="$2"; shift 2 ;;
*) shift ;;
esac
done
[ -z "$COMPANY" ] && { echo "--company is required"; exit 1; }
[ -z "$INDUSTRY" ] && { echo "--industry is required (hvac|banking|telecom|healthcare|retail-merchandising|other)"; exit 1; }
[ -z "$WEBSITE" ] && { echo "--website is required"; exit 1; }
# Default template name
if [ -z "$TEMPLATE_NAME" ]; then
CLEAN_COMPANY=$(echo "$COMPANY" | tr -cd '[:alnum:]_')
CLEAN_INDUSTRY=$(echo "$INDUSTRY" | tr '[:lower:]' '[:upper:]' | head -c 1)$(echo "$INDUSTRY" | tail -c +2)
TEMPLATE_NAME="PreWorkBrief_${CLEAN_INDUSTRY}_${CLEAN_COMPANY}"
fi
```
---
## Step 1: Build the business context
Combine the company website with the admin's description into a short technician-facing brief.
**1a. Fetch the website.**
Use Claude's `WebFetch` tool against `$WEBSITE` with the prompt:
> Read this company's homepage and any obvious sub-pages. Return a 5-bullet summary of (1) what the company does, (2) the products or services they sell, (3) who their typical customer is, (4) what kind of work a field technician would perform on-site for them, (5) any specialized equipment, certifications, or terminology a technician would need to know. Keep it factual and pulled directly from the page. Do not invent details.
Capture the response as `$WEBSITE_SUMMARY`.
**1b. Get the admin's description.**
If `--description` was passed, use it directly. Otherwise ask:
> "In 1-2 sentences, what do your technicians actually do on-site for this business?"
Capture as `$ADMIN_DESCRIPTION`.
**1c. Synthesize.**
Combine `$WEBSITE_SUMMARY` and `$ADMIN_DESCRIPTION` into a single 3-5 sentence paragraph saved as `$BUSINESS_CONTEXT`. Show the result to the admin and ask for sign-off before continuing.
---
## Step 2: Load the vertical seed template
```bash
SEED_PATH="templates/${INDUSTRY}.md"
if [ -f "$SEED_PATH" ]; then
echo "✓ Found seed template at $SEED_PATH"
SEED=$(cat "$SEED_PATH")
else
echo "No seed for industry '$INDUSTRY'. Synthesizing one from business context."
INDUSTRY="other"
SEED=""
fi
```
The seed contributes:
- **Section structure** for the prompt template (e.g., HVAC = Mission and contact / Customer and SLA / Site access and certifications / Equipment and refrigerant context).
- **Recommended custom objects** with the fields each typically carries.
- **Standard objects + fields** to query in the flow.
- **Cadence example** to anchor the model's tone.
If the seed doesn't exist, the skill writes one at the end of the run so the next admin in the same vertical gets it.
---
## Step 3: Audit the org's existing schema
Some of what the seed proposes may already exist in the org. Audit before creating duplicates.
```bash
GROUNDING_OBJECTS="Account Asset Contact Case WorkOrder WorkOrderLineItem ServiceAppointment WorkPlan WorkStep"
EXCLUDE_NAMESPACES="FSL__,FSSK__,SDO__,FSLDemoTools__"
for obj in $GROUNDING_OBJECTS; do
echo ""
echo "Custom fields on $obj:"
sf sobject describe --sobject "$obj" --target-org "$ORG_ALIAS" --json 2>/dev/null | \
jq -r --arg obj "$obj" --arg exclude "$EXCLUDE_NAMESPACES" '
($exclude | split(",")) as $ns_list |
.result.fields[]?
| select(.name | endswith("__c"))
| . as $f
| select(any($ns_list[]; . as $ns | $f.name | startswith($ns)) | not)
| " \($obj).\(.name) | type=\(.type) | label=\(.label)"
'
done > /tmp/pwb-existing-customs.txt
# Check whether the seed's recommended custom objects already exist
sf sobject list --target-org "$ORG_ALIAS" --sobject-type custom --json | \
jq -r '.result[]?' > /tmp/pwb-existing-objects.txt
cat /tmp/pwb-existing-customs.txt
echo ""
echo "Existing custom objects: $(wc -l < /tmp/pwb-existing-objects.txt)"
```
Cross-reference against the seed's recommended objects. For each recommended object:
- Already exists → skip object creation, audit its fields.
- Doesn't exist → propose to admin for creation.
Show the diff to the admin and ask for confirmation before deploying anything.
---
## Step 4: Generate and deploy custom objects
For each approved custom object, write metadata files. The general shape (using HVAC's `Refrigerant_Log__c` as an illustration; the actual fields come from the seed):
```xml
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<deploymentStatus>Deployed</deploymentStatus>
<description>{seed-supplied description}</description>
<enableActivities>true</enableActivities>
<enableHistory>true</enableHistory>
<enableReports>true</enableReports>
<enableSearch>true</enableSearch>
<label>{seed-supplied label}</label>
<nameField>
<label>{Object} Number</label>
<type>AutoNumber</type>
<displayFormat>{prefix}-{0000}</displayFormat>
<startingNumber>1</startingNumber>
</nameField>
<pluralLabel>{seed-supplied plural label}</pluralLabel>
<sharingModel>ReadWrite</sharingModel>
</CustomObject>
```
Lookup field rules to avoid the "must specify either cascade delete or restrict delete" deploy error:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Asset__c</fullName>
<deleteConstraint>SetNull</deleteConstraint>
<label>Asset</label>
<referenceTo>Asset</referenceTo>
<relationshipLabel>Refrigerant Logs</relationshipLabel>
<relationshipName>Refrigerant_Logs</relationshipName>
<type>Lookup</type>
</CustomField>
```
Note: do not set `<required>true</required>` on lookups — pair it with `<deleteConstraint>` or the deploy fails. For the customs the brief queries, optional lookups are fine.
If any new lookup goes onto WorkOrder (e.g., `WorkOrder.Maintenance_Contract__c`), write that field too — the flow will reference it via `$Input.WorkOrder.<New_Field>__c`.
Deploy:
```bash
mkdir -p /tmp/pwb-custom-build/force-app/main/default
cat > /tmp/pwb-custom-build/sfdx-project.json <<'EOF'
{"packageDirectories":[{"path":"force-app","default":true}],"namespace":"","sourceApiVersion":"65.0"}
EOF
# (Write all object + field XMLs into /tmp/pwb-custom-build/force-app/main/default/objects/...)
cd /tmp/pwb-custom-build
sf project deploy start --target-org "$ORG_ALIAS" --source-dir force-app --wait 30 --api-version 65.0
```
---
## Step 5: Generate the from-scratch flow
Write a `PromptFlow` flow that grounds on the relevant standard objects + the new customs. The pattern (verified working against the `afvuser` trial org, 2026-05-20):
- `<processType>PromptFlow</processType>`
- `<apiVersion>65.0</apiVersion>` (the managed flow's `getCustomerSignalsInsights` action requires v65+; from-scratch flows that don't use it can stay lower, but 65 is the safe default)
- `<start>` block has `<triggerType>Capability</triggerType>` and a `<capabilityTypes>` element declaring `PromptTemplateType://einstein_gpt__fieldServicePreWorkBrief` with a `WorkOrder` SObject input
- A chain of `<recordLookups>` elements with `getFirstRecordOnly=true` (single-record references resolve cleanly; collections require Loops which complicate the demo)
- An `<assignments>` element with `<elementSubtype>AddPromptInstructions</elementSubtype>` that builds `$Output.Prompt` from grounded references
Skeleton:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>65.0</apiVersion>
<description>{Industry}-specific Pre-Work Brief grounding flow for {Company}.</description>
<interviewLabel>Pre-Work Brief {Industry} {!$Flow.CurrentDateTime}</interviewLabel>
<label>Pre-Work Brief {Industry} Custom</label>
<processType>PromptFlow</processType>
<status>Active</status>
<start>
<locationX>0</locationX>
<locationY>0</locationY>
<capabilityTypes>
<name>PromptTemplateType://einstein_gpt__fieldServicePreWorkBrief</name>
<capabilityName>PromptTemplateType://einstein_gpt__fieldServicePreWorkBrief</capabilityName>
<inputs>
<name>WorkOrder</name>
<capabilityInputName>WorkOrder</capabilityInputName>
<dataType>SOBJECT://WorkOrder</dataType>
<isCollection>false</isCollection>
</inputs>
</capabilityTypes>
<connector>
<targetReference>{first lookup}</targetReference>
</connector>
<triggerType>Capability</triggerType>
</start>
<!-- recordLookups, then a single assignments that builds $Output.Prompt -->
</Flow>
```
The seed at `templates/<industry>.md` enumerates which lookups to chain, what each queries, and how `$Output.Prompt` is structured.
Deploy:
```bash
cd /tmp/pwb-custom-build
sf project deploy start --target-org "$ORG_ALIAS" --source-dir force-app/main/default/flows --wait 30 --api-version 65.0
```
Activate the flow (deploying with `<status>Active</status>` is necessary but not sufficient — Salesforce treats it as draft until explicitly activated):
```bash
FD_ID=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
--query "SELECT Id FROM FlowDefinition WHERE DeveloperName = '${TEMPLATE_NAME}_Flow'" --json | \
jq -r '.result.records[0].Id')
LATEST_VERSION=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
--query "SELECT VersionNumber FROM Flow WHERE Definition.DeveloperName = '${TEMPLATE_NAME}_Flow' ORDER BY VersionNumber DESC LIMIT 1" --json | \
jq -r '.result.records[0].VersionNumber')
cat > /tmp/activate-flow.json <<EOF
{"Metadata": {"activeVersionNumber": $LATEST_VERSION}}
EOF
sf api request rest --target-org "$ORG_ALIAS" --method PATCH \
"/services/data/v65.0/tooling/sobjects/FlowDefinition/$FD_ID" \
--body @/tmp/activate-flow.json
```
---
## Step 6: Generate the prompt template
Build a `GenAiPromptTemplate` with three sections from the seed: business-context paragraph, flow reference, instruction block (sections + rules + cadence example). The flow reference is the single line:
```
{!$Flow:<flow-developer-name>.Prompt}
```
Skeleton:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<GenAiPromptTemplate xmlns="http://soap.sforce.com/2006/04/metadata">
<developerName>{TEMPLATE_NAME}</developerName>
<masterLabel>Pre-Work Brief — {Company} ({Industry})</masterLabel>
<templateVersions>
<content>You are briefing a {Industry} field technician on the job they are about to perform on-site. Address the technician directly, in the second person.
Business context (do not contradict):
{BUSINESS_CONTEXT}
Specific job grounding:
{!$Flow:{flow-developer-name}.Prompt}
{INSTRUCTION_BLOCK from seed — sections + rules + cadence example}
</content>
<inputs>
<apiName>WorkOrder</apiName>
<definition>SOBJECT://WorkOrder</definition>
<referenceName>Input:WorkOrder</referenceName>
<required>true</required>
</inputs>
<primaryModel>sfdc_ai__DefaultOpenAIGPT4OmniMini</primaryModel>
<status>Published</status>
<templateDataProviders>
<definition>flow://{flow-developer-name}</definition>
<parameters>
<definition>SOBJECT://WorkOrder</definition>
<isRequired>true</isRequired>
<parameterName>WorkOrder</parameterName>
<valueExpression>{!$Input:WorkOrder}</valueExpression>
</parameters>
<referenceName>Flow:{flow-developer-name}</referenceName>
</templateDataProviders>
</templateVersions>
<type>einstein_gpt__fieldServicePreWorkBrief</type>
<visibility>Global</visibility>
</GenAiPromptTemplate>
```
Deploy and surface the activation deeplink:
```bash
cd /tmp/pwb-custom-build
sf project deploy start --target-org "$ORG_ALIAS" --source-dir force-app/main/default/genAiPromptTemplates --wait 30 --api-version 65.0
# Resolve the template Id
TEMPLATE_ID=$(sf api request rest "/services/data/v62.0/einstein/prompt-templates?pageSize=200" --target-org "$ORG_ALIAS" 2>/dev/null | \
python3 -c "
import sys, json, os
d = json.loads(sys.stdin.read())
name = os.environ.get('TEMPLATE_NAME')
for t in d.get('promptRecords', []):
if t.get('fields', {}).get('DeveloperName', {}).get('value') == name:
print(t.get('fields', {}).get('Id', {}).get('value'))
break")
# Fall back to deploy report if runtime catalog hasn't picked it up yet
if [ -z "$TEMPLATE_ID" ]; then
for did in $(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
--query "SELECT Id FROM DeployRequest WHERE NumberComponentsDeployed > 0 ORDER BY CompletedDate DESC LIMIT 10" --json | \
jq -r '.result.records[]?.Id'); do
cand=$(sf project deploy report --target-org "$ORG_ALIAS" --job-id "$did" --json 2>/dev/null | \
jq -r ".result.details.componentSuccesses[]? | select(.componentType == \"GenAiPromptTemplate\" and .fullName == \"$TEMPLATE_NAME\") | .id" | head -1)
[ -n "$cand" ] && TEMPLATE_ID="$cand" && break
done
fi
echo ""
echo " ⚠ Action required: click Activate in Prompt Builder."
echo " The template is deployed and Published, but until activated, it"
echo " won't appear in the runtime catalog. The mobile app will fail with"
echo " 'We hit a snag' until activation is complete."
echo ""
echo " Generating sign-in link to the template (valid ~15 minutes):"
sf org open --target-org "$ORG_ALIAS" \
--path "/lightning/setup/EinsteinPromptStudio/$TEMPLATE_ID/edit" \
--url-only 2>&1 | grep -oE 'https://[^[:cntrl:][:space:]]+frontdoor[^[:cntrl:][:space:]]+' | head -1 | sed 's/\x1b\[[0-9;]*m//g'
```
Pause until the admin confirms activation. Verify:
```bash
sf api request rest "/services/data/v62.0/einstein/prompt-templates?pageSize=200" --target-org "$ORG_ALIAS" 2>/dev/null | \
python3 -c "
import sys, json, os
d = json.loads(sys.stdin.read())
name = os.environ.get('TEMPLATE_NAME')
hit = [t for t in d.get('promptRecords', []) if t.get('fields', {}).get('DeveloperName', {}).get('value') == name]
print('✓ Active in runtime catalog' if hit else '✗ Not yet active — click Activate in Prompt Builder')"
```
> **Why a click is still required.** Salesforce does not currently expose a programmatic activation path for `GenAiPromptTemplate`. We tried Tooling REST PATCH on `IsActive`, Connect API `/activate` endpoints across v62-v66, Apex `ConnectApi.EinsteinLLM` methods, and metadata `activeVersionNumber` — none work for `GenAiPromptTemplate` (the `activeVersionNumber` PATCH does work for Flow, which is why Step 5 can activate the flow programmatically). The skill follows the same one-click activation pattern the base `setting-up-pre-work-brief` skill uses for consistency.
---
## Step 7: Generate the permission set
Bundle CRUD on the new objects, FLS on the new fields (including any new WorkOrder lookups), and assign to admin + technician:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<description>Read/Edit on {Industry} custom objects + new fields used by the {Industry} Pre-Work Brief flow for {Company}.</description>
<hasActivationRequired>false</hasActivationRequired>
<label>Pre-Work Brief {Industry} Access ({Company})</label>
<objectPermissions>
<allowCreate>true</allowCreate>
<allowDelete>true</allowDelete>
<allowEdit>true</allowEdit>
<allowRead>true</allowRead>
<modifyAllRecords>false</modifyAllRecords>
<object>{Custom_Object__c}</object>
<viewAllRecords>true</viewAllRecords>
</objectPermissions>
<!-- Repeat objectPermissions for each new custom object -->
<fieldPermissions><editable>true</editable><field>{Object}.{Field__c}</field><readable>true</readable></fieldPermissions>
<!-- Repeat fieldPermissions for each new field -->
</PermissionSet>
```
Deploy + assign:
```bash
cd /tmp/pwb-custom-build
sf project deploy start --target-org "$ORG_ALIAS" --source-dir force-app/main/default/permissionsets --wait 30 --api-version 65.0
PERMSET_NAME="PreWorkBrief_${INDUSTRY}_Access"
sf org assign permset --name "$PERMSET_NAME" --target-org "$ORG_ALIAS"
sf org assign permset --name "$PERMSET_NAME" --on-behalf-of "$TECH_USERNAME" --target-org "$ORG_ALIAS"
```
> **Asset.ProductDescription gotcha.** Verified during the HVAC dry-run on `afvuser` (2026-05-19): even with full Asset CRUD granted, `ProductDescription` is read-only for non-admin profiles in some org configurations. If the seed proposes populating it, fall back to writing the same content into `Asset.Description` instead.
---
## Step 8: Create a fresh test Work Order with seed data
Mirrors the `setting-up-pre-work-brief` Step 8-fresh pattern. Resolve Service Resource + Service Territory dynamically; create Account → Asset → custom-object records → Contact → WO → SA → AssignedResource as a chain so the brief has real data to ground on.
The seed contributes example values for the test record (e.g., HVAC seed populates a 10-ton rooftop unit, Platinum SLA contract, R-410A refrigerant log at 14.2 lbs).
After creation, hand off to the admin:
> The {Industry} Pre-Work Brief is wired up. Open the Field Service mobile app, sign in as **`$TECH_USERNAME`**, navigate to today's schedule. The new Work Order **`$WO_NUMBER`** should appear. Open it; the Pre-Work Brief should render in the Overview tab and reference {Company}'s business context plus the custom-object data this skill just created.
---
## Adding a new vertical seed
If you ran with `--industry other` and want to contribute the synthesized template back, the skill writes a draft to `templates/<your-industry>.md` at the end of the run. Review the draft, edit as needed, and commit. The next admin in your industry skips the synthesis step.
A seed should contain:
| Section | What it specifies |
|---|---|
| **Business archetype** | One-sentence description of the vertical's typical customer + technician work |
| **Recommended custom objects** | 2-4 objects with field lists. Include why each matters (compliance, SLA, asset history). |
| **Standard objects + fields to query** | Which standard fields the flow's Get-record elements should pull (e.g., `ServiceAppointment.ArrivalWindowStartTime`, `Contact.Email`) |
| **Prompt template section structure** | The 3-5 sections the brief should contain (e.g., HVAC = Mission and contact / Customer and SLA / Site access and certifications / Equipment and refrigerant context) |
| **Rules** | Vertical-specific rules (e.g., HVAC = "if last refrigerant service was >12 months ago, call it out") |
| **Cadence example** | Tone-anchoring example paragraph using the section structure |
| **Test data sample** | Realistic values for the test WO + seed records the skill creates in Step 8 |
See `templates/hvac.md` as the canonical example.
---
## Idempotency
Re-running this skill against the same org with the same `--customized-template-name`:
- Re-fetches the website and re-prompts for description (cheap; admins re-confirm).
- Re-audits org schema; new customs may have appeared since last run.
- Skips object creation if Step 4 finds the customs already exist.
- Re-deploys the flow as a new version (Salesforce treats identical metadata as no-op).
- Re-deploys the prompt template (same; identical metadata is a no-op).
- Creates a **new** test WO + SA every run.
For a second vertical in the same org, pass a different `--industry` and `--customized-template-name`. The skill produces a parallel pipeline (objects + flow + permset + template + test WO) without touching the first.
---
## Related afv-library Skills
- `setting-up-pre-work-brief` — required prerequisite. Runs the base setup that this skill builds on.
- `generating-flow` — useful when the from-scratch flow needs deeper edits than this skill performs (e.g., complex Loops over collections, Decision elements). Open the deployed flow in Flow Builder for visual review.
- `generating-permission-set` — for orgs that already have a permission set pattern to extend rather than create anew.
---
## References
- Pre-Work Brief setup: `https://help.salesforce.com/s/articleView?id=service.mfs_einstein_pre_work_brief.htm`
- Pre-Work Brief data model: `https://help.salesforce.com/s/articleView?id=service.mfs_einstein_pre_work_brief_data.htm`
- Prompt Builder: `https://help.salesforce.com/s/articleView?id=ai.prompt_builder_about.htm`
- Flow Reference (PromptFlow processType): `https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_visual_workflow.htm`
---
## Known Limitations
- Programmatic activation of `GenAiPromptTemplate` is not exposed in the API today. Step 6's deeplink is the supported path until Salesforce ships an Activate REST endpoint. The skill follows the same one-click pattern as `setting-up-pre-work-brief` for consistency.
- Flows with multi-record record lookups (`getFirstRecordOnly=false`) require a Loop element to reference individual records in the Output.Prompt assignment; the from-scratch pattern in Step 5 uses `getFirstRecordOnly=true` to keep the demo simple. For verticals where a Work Order has multiple Refrigerant Logs / Compliance Checks / etc., the seed should specify a Loop pattern.
- `WebFetch` results depend on the company's homepage being public and not paywalled or JS-only. Pages rendered entirely by client-side JS may return little usable content; in that case fall back to admin description only.
- `AssignedResource` create may fail in orgs that require a Service Territory on the Service Appointment. Step 8 resolves Service Territory from the chosen Service Resource's `ServiceTerritoryMember` and includes it on create. If the resource has no territory membership, ask the admin to pick a different resource or assign a territory in Setup.
- `Asset.ProductDescription` is read-only for non-admin profiles in some org configurations (verified on `afvuser` 2026-05-19). Seeds should write descriptive text to `Asset.Description` instead.