* @W-21955450@ Rename topic to subagent for Agent Script v2
Aligns with Agent Script v2 naming standards where `topic` is renamed
to `subagent` across all skill documentation and templates.
Changes:
- Agent Script templates: topic keyword → subagent keyword
- References: @topic.* → @subagent.*
- Documentation: Updated all skill references and guides
- Natural language references preserved in comments/descriptions
* Rename start_agent topic_selector to agent_router
Completes the topic → subagent terminology alignment by:
1. Renaming start_agent from topic_selector to agent_router (15 agent files)
2. Updating template topic declarations: topic {{placeholder}} → subagent {{placeholder}} (5 files)
3. Updating all @subagent.topic_selector references to @subagent.agent_router (35 occurrences)
4. Updating documentation: prose, examples, and diagrams (10 markdown files)
5. Updating comments to use agent_router terminology
Files affected:
- 22 agent template files
- 10 documentation/reference markdown files
- Template component files
The agent_router name is more descriptive of its actual function
(routing to different subagents) and completes the Agent Script v2
terminology standardization.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Rename files with "topic" to use "subagent" terminology
Completes the topic → subagent terminology alignment by renaming
files and updating all references:
**Files renamed (5):**
- multi-topic.agent → multi-subagent.agent
- template-single-topic.agent → template-single-subagent.agent
- template-multi-topic.agent → template-multi-subagent.agent
- topic-with-actions.agent → subagent-with-actions.agent
- agent-topic-map-diagrams.md → agent-subagent-map-diagrams.md
**References updated (6 docs):**
- Updated all filename references to point to new filenames
- Updated "Topic Map" → "Subagent Map" throughout documentation
- Updated "multi-topic"/"single-topic" → "multi-subagent"/"single-subagent"
Files modified:
- README.md, SKILL.md, agent-spec-template.md
- assets/agents/README.md, assets/README-legacy.md
- references/agent-design-and-spec-creation.md
This ensures consistent "subagent" terminology across filenames,
file content, and all documentation references.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Complete topic-to-subagent terminology update across skills
Comprehensive update replacing "topic" with "subagent" terminology throughout
the developing-agentforce and testing-agentforce skills to align with Agent
Script's `subagent` block naming.
Key changes:
- "Topic Selector" → "Subagent Router" in all agent templates and docs
- "Topic/action" → "Subagent/action" in documentation
- "Topic map" → "Subagent map" in diagram references
- Updated all architecture documentation to use "subagent" terminology
- Updated 19 .agent template files with new labels and comments
- Updated 8 reference documentation files with consistent terminology
API contract preservation:
- Test spec YAML files preserve "topic" terminology to match Testing Center API
- Added clarifying comments explaining topic/subagent equivalence in YAML files
- Field names like `expectedTopic` unchanged (Salesforce API requirement)
Preserved terms:
- "off-topic" (standard phrase for out-of-scope)
- "expectedTopic" field (Testing Center API)
- "platform topics" (Salesforce guardrail features)
32 files changed, 379 insertions(+), 366 deletions(-)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Complete comprehensive topic-to-subagent terminology update
Thorough update replacing all remaining "topic" references with "subagent"
terminology across developing-agentforce, testing-agentforce, and
observing-agentforce skills to fully align with Agent Script's `subagent`
block naming.
Key changes:
- Agent Script syntax: @topic.<name> → @subagent.<name>
- Agent Script syntax: topic.actions → subagent.actions
- Shell script patterns: ^topic → ^subagent
- Documentation: "topic instructions" → "subagent instructions"
- observing-agentforce skill: Updated all agent architecture references
- Template files: Updated all inline comments and descriptions
- Variable names in scripts: TOPIC → SUBAGENT
Specific updates:
- 45 files changed, 294 insertions, 294 deletions
- Updated all Agent Script code examples to use @subagent syntax
- Updated observing-agentforce issue classification guide
- Updated shell script patterns in diagnostic tools
- Updated Apex comments to clarify topic field maps to subagents
Preserved (as required):
- "off-topic" and "off_topic" (standard out-of-scope phrase)
- Testing Center API fields: expectedTopic, topic: in YAML
- API response fields: .topic, generatedData.topic, topic_assertion
- STDM field names: ssot__TopicApiName__c (with clarifying docs)
- Template placeholders in test specs (API values)
- "Topic hash drift" (API field behavior)
- "Email topic/purpose" (means email subject)
- Explanatory comments about API field mapping
All Agent Script syntax and documentation now consistently uses "subagent"
while preserving backward compatibility with platform API field names.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* a few more topic -> subagent replacements
---------
Co-authored-by: Steve Hetzel <shetzel@salesforce.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
16 KiB
Phase 3: Improve -- Edit .agent File (Full Reference)
Phase 3 edits the .agent file directly using the Edit tool. No intermediate markdown conversion step. After editing, validate and publish the authoring bundle.
Pre-Flight: Verify Action Target Availability
Before making any .agent file edits, verify that all action targets actually exist and are registered in the org.
Step 1 -- Extract all action targets from the .agent file:
AGENT_FILE="<path_to_agent_file>"
grep -oP 'target:\s*"\K[^"]+' "$AGENT_FILE" | sort -u
Step 2 -- Query GenAiFunction records in the org:
sf data query --json -q "SELECT DeveloperName, MasterLabel, InvocableActionDeveloperName FROM GenAiFunction WHERE IsActive = true" -o <ORG_ALIAS>
Step 3 -- Compare and flag missing targets:
# For flow:// targets
sf flow list -o <ORG_ALIAS> --json | python3 -c "import json,sys; flows=[f['ApiName'] for f in json.load(sys.stdin)['result']]; print('\n'.join(flows))"
# For apex:// targets
sf data query --json -q "SELECT Name FROM ApexClass WHERE Name IN ('ClassName1','ClassName2')" -o <ORG_ALIAS>
Step 4 -- Present options to user if targets are missing:
- Deploy missing targets first -- Use
Section 17 of /developing-agentforceto generate stubs, thenSection 18 of /developing-agentforceto deploy - Remove unresolvable actions -- Delete from
.agentfile and focus on routing/instruction improvements - Register via Agent Builder UI -- For targets that exist but aren't registered as
GenAiFunction - Proceed anyway -- If the planned fix only touches routing logic or instructions
Guideline: If 50%+ of action targets are missing or unregistered, pivoting to routing and instruction fixes is usually the most pragmatic path.
WARNING: Do NOT use flow:// syntax directly in .agent file action target: URIs as a workaround -- the Agent Script lexer does not support URI prefixes in target fields.
.agent File Structure
The .agent file uses Agent Script -- a tab-indented DSL that compiles to Agentforce metadata:
system:
instructions: "Agent-level system prompt (persona, guardrails)"
messages:
welcome: "Welcome message"
error: "Error fallback message"
config:
agent_name: "AgentApiName"
agent_label: "Agent Display Name"
description: "Agent description"
default_agent_user: "user@org.com"
variables:
myVar: mutable string
description: "Variable description"
default: ""
start_agent: entry_topic
subagent entry_topic:
label: "Entry Subagent"
description: "Routes users to specialized subagents"
reasoning:
instructions: ->
| Welcome the user warmly.
| Ask how you can help today.
actions:
go_to_orders: @utils.transition to @subagent.orders
description: "Route to orders subagent"
check_order: @actions.get_order_status
description: "Look up order details"
with order_id = @variables.order_id
set @variables.order_status = @outputs.status
Critical mapping to Salesforce metadata:
subagent.description->GenAiPluginDefinition.Description(subagent routing signal)subagent.reasoning.instructions->GenAiPluginInstructionDef.Instruction(verbatim LLM prompt text)system.instructions->GenAiPlannerDefinition.Description(agent-level system prompt)reasoning.actionswith@utils.transition-> subagent transitionsreasoning.actionswith@actions.*-> action invocations withwith(input) andset(output) bindings
Map Issue to Fix Location
| Root cause category | STDM signal | Fix target in .agent file | What to change |
|---|---|---|---|
Agent Configuration Gap |
Subagent misroute | subagent <name>: description: |
Tighten description to exclude overlapping intents |
Agent Configuration Gap |
Action not called | subagent <name>: reasoning: actions: and reasoning: instructions: |
Add action definition under actions: and mention it in instructions: |
Agent Configuration Gap |
Wrong action input / error | reasoning: actions: <action>: with |
Correct with bindings or action target: URI |
Agent Configuration Gap |
Variable not captured | reasoning: actions: <action>: set |
Add set @variables.myVar = @outputs.field binding |
Agent Configuration Gap |
No post-action transition | reasoning: actions: |
Add @utils.transition to @subagent.<next_subagent> action |
Agent Configuration Gap |
LOW adherence / vague instructions | subagent <name>: reasoning: instructions: |
Rewrite using instruction principles below |
Agent Configuration Gap |
Identical instructions across subagents | All subagent: reasoning: instructions: blocks |
Give each subagent distinct, actionable instructions |
Knowledge Gap -- Infrastructure |
Knowledge question answered generically | Add knowledge action definition to the relevant subagent | Define action with retriever:// target |
Knowledge Gap -- Content |
Knowledge question -- wrong/missing answer | N/A (org data issue) | Add missing articles to knowledge space |
Platform / Runtime Issue |
Action timeout / latency > 10s | Flow or Apex class (not .agent) | Optimize query/processing logic |
Agent Configuration Gap |
Dead hub anti-pattern | Entire intermediate subagent block | Move transitions to start_agent > reasoning > actions:, delete dead hub subagent |
Target resolution checklist:
| Target exists? | Registered as GenAiFunction? | Action |
|---|---|---|
| Yes | Yes | Issue is elsewhere (check action bindings, instructions) |
| Yes | No | Deploy/register: use Section 18 of /developing-agentforce or register via Agent Builder UI |
| No | N/A | Scaffold first: use Section 17 of /developing-agentforce to generate stub, then deploy |
| Can't deploy now | N/A | Pivot to routing fixes: remove action from .agent, focus on instructions and transitions |
Principles for Effective Subagent Instructions
Good instructions are specific, imperative, and action-named. Poor instructions are persona descriptions or generic guidance reused across subagents.
- Name the action explicitly -- "Use
@actions.schedule_test_driveto book the appointment" not "help the user book" - State the pre-condition -- "Only handle scheduling after the customer's name and email have been collected"
- State what to do after -- "After scheduling completes, confirm the date/time and transition to follow_up"
- Scope tightly -- "This subagent handles test drive scheduling only. For vehicle specs or pricing, do not answer -- the user should be routed to general_support"
- Keep persona out of instructions -- persona belongs in
system: instructions:(agent-level), not per-subagent reasoning instructions - One responsibility per subagent -- if the instruction covers 3 distinct tasks, split into 3 subagents
Before / after example (identical instructions -> distinct instructions):
Before (generic persona text, same across all subagents):
reasoning:
instructions: |
You are Nova, a friendly Tesla support assistant. Greet customers warmly,
help them with their needs, and guide them toward scheduling a test drive.
After (for identity_collection subagent specifically):
reasoning:
instructions: ->
| Collect the customer's name, email address, and phone number using @actions.collect_customer_info.
| Do not proceed until all three fields are provided.
| After collection, confirm the details back to the customer.
actions:
collect_info: @actions.collect_customer_info
description: "Capture customer contact details"
set @variables.customer_name = @outputs.name
set @variables.customer_email = @outputs.email
proceed: @utils.transition to @subagent.schedule_test_drive
description: "Move to test drive scheduling after info collected"
available when @variables.customer_name != ""
Regression Prevention
When editing subagent instructions, follow these principles:
-
Establish a baseline BEFORE editing -- Run the test utterance 3 times before making changes. Record the pass rate.
-
Make minimal, targeted edits -- Change only the specific instruction line that addresses the identified issue. Do NOT expand terse instructions into verbose ones unless the terse version was causing a specific documented failure.
-
Avoid instruction expansion -- Adding more text to instructions does NOT always help. Prefer:
- Adding a single action reference: "Use
@actions.Xto look up..." - Adding a single constraint: "Do not proceed until the customer provides..."
- Adding a single routing directive: "After completing, transition to @subagent.Y"
- Adding a single action reference: "Use
-
Test immediately after each edit -- Run the same test utterances. If pass rate drops, revert the change immediately.
-
One fix per publish cycle -- Do not batch multiple instruction changes into a single publish.
-
Check cross-subagent dependencies before editing -- Before changing Subagent A, identify variable dependencies, transition chains, and shared variable mutations:
grep -n 'set @variables\.' "$AGENT_FILE" grep -n 'with .* = @variables\.' "$AGENT_FILE" grep -n '@utils.transition to @subagent\.' "$AGENT_FILE" -
Test adjacent subagents after each fix -- Include at least one cross-subagent test to confirm the fix didn't cause spillover routing.
-
Verify start_agent routing after subagent removal -- If removing a dead hub or merging subagents, verify
start_agent > reasoning > actions:still has transition actions to all remaining subagents.
Apply Fixes
Step 1 -- Read the current .agent file using the Read tool. Locate the specific subagent block that needs changes.
Step 2 -- Edit the .agent file directly using the Edit tool. Edit only the specific lines that need to change. Common edit patterns:
- Subagent description (for misroute fixes): Change
description:text - Subagent instructions (for LOW adherence): Replace
reasoning: instructions:block - Adding an action: Add definition under
reasoning: actions: - Adding a transition: Add
@utils.transition to @subagent.<name>action - Adding an
available whenguard: Add guard condition to action definition
IMPORTANT: Agent Script uses tabs for indentation, not spaces.
Step 3 -- Show the diff:
cd <project-root> && git diff <AGENT_FILE>
Validate, Deploy, Publish, and Activate
After editing the .agent file, use this deployment chain. Never update GenAiPluginInstructionDef or other agent metadata directly -- always edit the .agent file and re-deploy.
# Step 1: Validate (dry run)
sf agent validate authoring-bundle --json --api-name <AGENT_API_NAME> -o <org>
If validation fails: fix syntax errors, deploy missing targets, or resolve duplicate names.
# Step 2: Publish (compiles, deploys metadata, and activates)
sf agent publish authoring-bundle --json --api-name <AGENT_API_NAME> -o <org>
If publish fails, use the deploy + activate fallback:
# Step 3a: Deploy the bundle
sf project deploy start --json --metadata "AiAuthoringBundle:<AGENT_API_NAME>" -o <org>
# Step 3b: Activate
sf agent activate --json --api-name <AGENT_API_NAME> -o <org>
Warning: deploy + activate is an incomplete fallback.
sf project deploy startstores the bundle metadata but does NOT propagate subagent-levelreasoning: actions:blocks to liveGenAiPluginDefinitionrecords. Always verify with--authoring-bundlepreview.
Never use the Tooling API to patch GenAiPluginInstructionDef or other BPO objects directly.
Verify
Immediate -- run the Phase 2 scenarios that returned [CONFIRMED] before the fix. All should now return [NOT REPRODUCED]. Use --authoring-bundle to get trace-level verification:
sf agent preview start --json --authoring-bundle <BundleName> -o <org> | tee /tmp/verify_start.json
SESSION_ID=$(python3 -c "import json; print(json.load(open('/tmp/verify_start.json'))['result']['sessionId'])")
sf agent preview send --json \
--session-id "$SESSION_ID" \
--utterance "<test utterance from Phase 2 scenario>" \
--authoring-bundle <BundleName> \
-o <org> | tee /tmp/verify_response.json
PLAN_ID=$(python3 -c "import json; d=json.load(open('/tmp/verify_response.json')); print(d['result']['messages'][-1]['planId'])")
TRACE=".sfdx/agents/<BundleName>/sessions/$SESSION_ID/traces/$PLAN_ID.json"
sf agent preview end --json --session-id "$SESSION_ID" --authoring-bundle <BundleName> -o <org>
Trace-based verification checklist:
# 1. Correct subagent routing
jq -r '.topic' "$TRACE"
# 2. Grounding passed (no UNGROUNDED)
jq -r '.plan[] | select(.type == "ReasoningStep") | .category' "$TRACE"
# 3. No UNGROUNDED retries (count should be 1)
jq '[.plan[] | select(.type == "ReasoningStep")] | length' "$TRACE"
# 4. Correct tools visible
jq -r '.plan[] | select(.type == "EnabledToolsStep") | .data.enabled_tools[]' "$TRACE"
# 5. Variable state updated correctly
jq -r '.plan[] | select(.type == "VariableUpdateStep") | .data.variable_updates[] | "\(.variable_name): \(.variable_new_value)"' "$TRACE"
At scale -- after 24-48 hours of new live sessions, re-run Phase 1 and compare against the pre-fix baseline:
| Metric | What to look for after fix |
|---|---|
| Subagents seen in STDM | Dead subagents should now appear in session data |
TRUST_GUARDRAILS_STEP value |
LOW occurrences should drop or disappear |
| Action invocation per turn | Actions should now fire for the intents they cover |
action_error_count |
Should not increase (regression check) |
| Avg session duration / turn count | Shorter = less confusion, faster resolution |
Safety Re-Verification (Required)
After applying fixes, re-run safety review on the modified .agent file. Optimization fixes can inadvertently introduce safety regressions:
- Relaxing
available whenguards may expose actions that should be gated - Expanding subagent descriptions may cause the agent to handle out-of-scope requests
- Changing instructions to be more permissive may weaken guardrails
- Adding literal instructions with tool names may bypass safety boundaries
Run the safety review from Section 15 of /developing-agentforce (Identity, User Safety, Data Handling, Content Safety, Fairness, Deception, Scope). Focus especially on:
- Scope boundaries -- Did the fix widen the agent's scope beyond what's appropriate?
- Guard conditions -- Did relaxing
available whenexpose sensitive actions? - Instruction safety -- Do new/modified instructions maintain appropriate guardrails?
- Escalation paths -- Are escalation paths still intact after subagent restructuring?
If any new BLOCK finding is introduced by the fix: revert and find an alternative fix. Do NOT deploy an agent with new safety violations.
Update Testing Center Test Cases
After fixing issues, create or update test cases in Testing Center format:
# tests/<AgentApiName>-regression.yaml
name: "<AgentApiName> Regression Tests"
subjectType: AGENT
subjectName: <AgentApiName>
testCases:
- utterance: "<exact utterance from Phase 2 scenario>"
expectedTopic: <subagent_that_should_handle_this>
expectedActions:
- <action_that_should_fire>
- utterance: "<another failing utterance>"
expectedTopic: <expected_subagent>
expectedOutcome: "Agent should <expected behavior description>"
Key format rules:
expectedActionsis a flat string list:["action_a"], NOT objectssubjectNameis the agent'sDeveloperName(API name without_vNsuffix)expectedOutcomeuses LLM-as-judge evaluation
Deploy and run:
sf agent test create --json \
--spec tests/<AgentApiName>-regression.yaml \
--api-name <AgentApiName>_Regression \
--force-overwrite \
-o <org>
sf agent test run --json \
--api-name <AgentApiName>_Regression \
--wait 10 \
--result-format json \
-o <org> | tee /tmp/regression_run.json
# ALWAYS use --job-id, NOT --use-most-recent which is broken
JOB_ID=$(python3 -c "import json; print(json.load(open('/tmp/regression_run.json'))['result']['runId'])")
sf agent test results --json --job-id "$JOB_ID" --result-format json -o <org>
All test cases derived from Phase 2 [CONFIRMED] issues should pass after the Phase 3 fix.