Read a Classic Workflow
Parse a Dataverse Classic Workflow XAML file and produce a human-readable
summary. This is almost always the first skill to invoke — every other skill
depends on understanding the workflow's structure first.
When to use
- "What does this workflow do?"
- "Summarize the workflow at
<path>"
- "List the steps in this workflow"
- "What entity does this workflow run on?"
- "Show me the trigger conditions"
- Any time another skill (analyze, compare, write) needs to first understand
the workflow before acting.
Inputs
- Required: Path to a
.xaml file containing a Classic Workflow.
- Optional: Path to the sibling
.xaml.data.xml metadata file. If
present, it provides the workflow's name, primary entity, trigger
bitmask, scope, mode, run-as, and statecode in friendlier form than the
XAML.
Outputs
A structured summary containing:
- Header — name, primary entity, category (must be Classic Workflow), mode.
- Activation context — trigger(s), filtering attributes, scope, run-as.
- State — Draft vs Activated.
- Step list — ordered, friendly descriptions of each user-visible step.
- Findings — any patterns from
reference/trigger-types.md worth
flagging (e.g. OnUpdate without filters).
Procedure
Step 1 — Locate and confirm the file
- If the user gave a path, read it. If they didn't, ask: "Which workflow
file? They're typically under
<solution>/src/Workflows/*.xaml after
pac solution unpack."
- Read the file's first ~50 lines.
- Confirm it's a Classic Workflow:
- Root must be
<mxswa:Activity> containing <mxswa:Workflow>.
- If you see
BusinessProcessFlow or BusinessRule in the root,
stop and tell the user: "This is a [BPF/Business Rule], not a
Classic Workflow. This agent doesn't handle that category."
Step 2 — Read the metadata sibling (if present)
Look for a <file-stem>.xaml.data.xml next to the XAML. It typically
contains XML elements for name, primaryentity, category, mode,
scope, runas, triggertypemask, triggeronupdateattributelist,
statecode, statuscode. Use these as the canonical source for header
metadata; fall back to the XAML root attributes if the sibling is missing.
Step 3 — Parse the activity tree
Read the entire XAML. Walk the tree from <mxswa:Workflow> → <Sequence>
and collect activities. Apply these rules from
reference/xaml-anatomy.md:
- Direct
mxswa:* elements → user-visible steps.
- Direct
mcwc:* elements → user-visible form-side steps.
mxswa:ActivityReference → look at AssemblyQualifiedName:
…ConditionSequence… → "Check Condition" (or "Wait Condition" if
Wait="True").
…ConditionBranch… → branch under a condition; render each branch's
children as a nested list.
…Composite… → step group; render the group's children as nested
steps under the Composite's DisplayName.
…TerminateWorkflow… → "Stop Workflow" with success/cancel reason.
- Internal helpers (
EvaluateExpression, EvaluateCondition,
EvaluateLogicalCondition, GetEntityProperty, SetEntityProperty,
RetrieveEntity, Persist, ConvertCrmXrmTypes) → collapse into
the user-visible step they belong to. Don't emit them as separate steps.
Step 3a — Recognize user-visible steps by their activity-tree fingerprint
The legacy designer treats certain combinations of low-level activities as
a single user-visible step. When you see one of these fingerprints inside a
Sequence or Composite, render it as one step and skip the children
you rolled up:
| Fingerprint (activities present in the same Sequence/Composite) |
Render as |
mxswa:UpdateEntity + one or more mxswa:SetEntityProperty (and the EvaluateExpression instances feeding them) |
"Update Record" — one step. Surface the entity name from UpdateEntity and the field/value pairs in friendly form. |
mxswa:CreateEntity + one or more mxswa:SetEntityProperty |
"Create Record" — one step. Same field/value rendering as Update. |
mxswa:CreateEntity (with EntityName="email") + mxswa:SendEmail |
"Send Email" — one step. Surface To / Cc / Subject / Body. |
Bare mxswa:SendEmail referencing a previously-created email variable |
"Send Email" (using existing email record) |
mxswa:ActivityReference AQN starts with …ConditionSequence and Wait is [False] or absent |
"Check Condition" — render its branches |
Same shape, but Wait="[True]" |
"Wait Condition" — background-only; render the wait predicate |
mxswa:ActivityReference AQN starts with …TerminateWorkflow or …StopWorkflow |
"Stop Workflow" — surface success/cancel + reason |
mxswa:ActivityReference to …PerformAction |
"Run Action: " — surface the input parameter mapping |
mxswa:ActivityReference to a Composite with a DisplayName |
A named step group — render the DisplayName as a heading and recurse into its children |
mxswa:ActivityReference to a Composite without a DisplayName |
An anonymous group — render its children inline at the parent level |
If you see an unrecognized AQN class name (one not listed in
reference/activity-types.md §4
or any of the BPF-internal classes called out there), treat it as a
Custom CodeActivity and surface the AQN to the user.
Step 4 — Render expressions in friendly form
For any VB bracket expression you encounter (subject lines, condition
operands, field assignments), render it in friendly token form per
reference/vb-expressions.md:
GetVariableValue(EntityProperty("name", "account"), "") → {Account: Name}
[True] / [False] → True / False
- A literal string → render the literal in quotes.
- An expression you can't simplify → render as
{expression: <truncated VB code>}.
Step 5 — Surface findings
Run the workflow against the patterns table in
reference/trigger-types.md §"Patterns to flag in summaries / reviews".
Report any matches as a "Findings" section at the bottom of the summary.
Output template
# {Workflow Display Name}
**Primary entity:** `{logical_name}`
**Category:** Classic Workflow
**Mode:** Background | Real-Time
**State:** Draft | Activated
## Trigger
- Trigger types: {OnCreate, OnUpdate, …}
- Filtering attributes (for OnUpdate): {list, or "(none — fires on every update)"}
- Scope: {User | BusinessUnit | ParentChildBU | Organization}
- Run as: {Owner | CallingUser}
## Steps
1. **{Step display name}** — {plain-English description}
- {sub-detail like "Set name to {Account: Name} & ' (reviewed)'"}
2. **Check Condition: {description}**
- **If True:**
1. {nested step}
2. {nested step}
- **Else:**
1. {nested step}
3. **Stop Workflow** — Status: Canceled. Reason: "{message}"
## Findings
- ⚠️ {finding} ({severity})
- …
(Or "No issues detected." if the workflow looks clean.)
Error handling
- File not found → ask the user to confirm the path. Suggest running
pac solution unpack if they haven't.
- Invalid XAML (parse error) → report the parse error verbatim. Do
not try to "fix" it silently. Ask the user to share the error message
with the source they exported from.
- Wrong category (BPF, Business Rule, Modern Flow) → as in Step 1,
stop and explain.
- Activity types you don't recognize → look them up in
reference/activity-types.md. If
still unknown, treat them as Custom CodeActivity (Section 8 of that
reference) and surface the AssemblyQualifiedName.
Don't
- Don't emit XAML in your summary unless the user asks for it. The point
of this skill is human-readable output.
- Don't echo the user's real entity logical names back into a generated
example. References in prose are fine; synthetic XAML examples must use
placeholders.
- Don't silently drop activity types. If you collapse a helper, mention
it ("Includes a checkpoint after step 3"). If you skip something
unrecognized, say so.
1---2name: dataverse-classic-read3description: Parse and summarize a Dataverse Classic Workflow XAML file (WF4, the `workflow` table) into a human-readable narrative. Use when user says "what does this workflow do", "summarize this workflow", "explain this XAML", "walk me through this workflow", "what triggers this", "what are the steps", "extract the trigger info", "is this real-time or background", "what scope does this run at", or pastes/points at a `.xaml` file from `pac solution clone` or `unpack`. Produces trigger + scope + mode + run-as details and a step-by-step narrative, resolving `mxswa:ActivityReference` wrappers and `[bracket]` VB.NET expressions into plain language. Do NOT use for editing XAML (use dataverse-classic-write), diffing two versions (use dataverse-classic-compare), or gap analysis against requirements (use dataverse-classic-analyze).4license: MIT-05---67# Read a Classic Workflow89**Parse a Dataverse Classic Workflow XAML file and produce a human-readable10summary.** This is almost always the first skill to invoke — every other skill11depends on understanding the workflow's structure first.1213---1415## When to use1617- "What does this workflow do?"18- "Summarize the workflow at `<path>`"19- "List the steps in this workflow"20- "What entity does this workflow run on?"21- "Show me the trigger conditions"22- Any time another skill (analyze, compare, write) needs to first understand23 the workflow before acting.2425## Inputs2627- **Required:** Path to a `.xaml` file containing a Classic Workflow.28- **Optional:** Path to the sibling `.xaml.data.xml` metadata file. If29 present, it provides the workflow's name, primary entity, trigger30 bitmask, scope, mode, run-as, and statecode in friendlier form than the31 XAML.3233## Outputs3435A structured summary containing:361. **Header** — name, primary entity, category (must be Classic Workflow), mode.372. **Activation context** — trigger(s), filtering attributes, scope, run-as.383. **State** — Draft vs Activated.394. **Step list** — ordered, friendly descriptions of each user-visible step.405. **Findings** — any patterns from `reference/trigger-types.md` worth41 flagging (e.g. OnUpdate without filters).4243---4445## Procedure4647### Step 1 — Locate and confirm the file48491. If the user gave a path, read it. If they didn't, ask: "Which workflow50 file? They're typically under `<solution>/src/Workflows/*.xaml` after51 `pac solution unpack`."522. Read the file's first ~50 lines.533. Confirm it's a Classic Workflow:54 - Root must be `<mxswa:Activity>` containing `<mxswa:Workflow>`.55 - If you see `BusinessProcessFlow` or `BusinessRule` in the root,56 **stop** and tell the user: "This is a [BPF/Business Rule], not a57 Classic Workflow. This agent doesn't handle that category."5859### Step 2 — Read the metadata sibling (if present)6061Look for a `<file-stem>.xaml.data.xml` next to the XAML. It typically62contains XML elements for `name`, `primaryentity`, `category`, `mode`,63`scope`, `runas`, `triggertypemask`, `triggeronupdateattributelist`,64`statecode`, `statuscode`. Use these as the canonical source for header65metadata; fall back to the XAML root attributes if the sibling is missing.6667### Step 3 — Parse the activity tree6869Read the entire XAML. Walk the tree from `<mxswa:Workflow>` → `<Sequence>`70and collect activities. Apply these rules from71[reference/xaml-anatomy.md](../dataverse-classic-workflow/reference/xaml-anatomy.md):7273- **Direct `mxswa:*` elements** → user-visible steps.74- **Direct `mcwc:*` elements** → user-visible form-side steps.75- **`mxswa:ActivityReference`** → look at `AssemblyQualifiedName`:76 - `…ConditionSequence…` → "Check Condition" (or "Wait Condition" if77 `Wait="True"`).78 - `…ConditionBranch…` → branch under a condition; render each branch's79 children as a nested list.80 - `…Composite…` → step group; render the group's children as nested81 steps under the Composite's `DisplayName`.82 - `…TerminateWorkflow…` → "Stop Workflow" with success/cancel reason.83- **Internal helpers** (`EvaluateExpression`, `EvaluateCondition`,84 `EvaluateLogicalCondition`, `GetEntityProperty`, `SetEntityProperty`,85 `RetrieveEntity`, `Persist`, `ConvertCrmXrmTypes`) → **collapse** into86 the user-visible step they belong to. Don't emit them as separate steps.8788### Step 3a — Recognize user-visible steps by their activity-tree fingerprint8990The legacy designer treats certain combinations of low-level activities as91a single user-visible step. When you see one of these fingerprints inside a92`Sequence` or `Composite`, render it as **one** step and skip the children93you rolled up:9495| Fingerprint (activities present in the same Sequence/Composite) | Render as |96|---|---|97| `mxswa:UpdateEntity` + one or more `mxswa:SetEntityProperty` (and the `EvaluateExpression` instances feeding them) | **"Update Record"** — one step. Surface the entity name from `UpdateEntity` and the field/value pairs in friendly form. |98| `mxswa:CreateEntity` + one or more `mxswa:SetEntityProperty` | **"Create Record"** — one step. Same field/value rendering as Update. |99| `mxswa:CreateEntity` (with `EntityName="email"`) + `mxswa:SendEmail` | **"Send Email"** — one step. Surface To / Cc / Subject / Body. |100| Bare `mxswa:SendEmail` referencing a previously-created email variable | **"Send Email"** (using existing email record) |101| `mxswa:ActivityReference` AQN starts with `…ConditionSequence` and `Wait` is `[False]` or absent | **"Check Condition"** — render its branches |102| Same shape, but `Wait="[True]"` | **"Wait Condition"** — background-only; render the wait predicate |103| `mxswa:ActivityReference` AQN starts with `…TerminateWorkflow` or `…StopWorkflow` | **"Stop Workflow"** — surface success/cancel + reason |104| `mxswa:ActivityReference` to `…PerformAction` | **"Run Action: <ActionName>"** — surface the input parameter mapping |105| `mxswa:ActivityReference` to a Composite with a `DisplayName` | A **named step group** — render the DisplayName as a heading and recurse into its children |106| `mxswa:ActivityReference` to a Composite **without** a DisplayName | An anonymous group — render its children inline at the parent level |107108If you see an unrecognized AQN class name (one not listed in109[reference/activity-types.md §4](../dataverse-classic-workflow/reference/activity-types.md#4-conditions-and-branching)110or any of the BPF-internal classes called out there), treat it as a111Custom CodeActivity and surface the AQN to the user.112113### Step 4 — Render expressions in friendly form114115For any VB bracket expression you encounter (subject lines, condition116operands, field assignments), render it in friendly token form per117[reference/vb-expressions.md](../dataverse-classic-workflow/reference/vb-expressions.md):118119- `GetVariableValue(EntityProperty("name", "account"), "")` → `{Account: Name}`120- `[True]` / `[False]` → `True` / `False`121- A literal string → render the literal in quotes.122- An expression you can't simplify → render as `{expression: <truncated VB code>}`.123124### Step 5 — Surface findings125126Run the workflow against the patterns table in127[reference/trigger-types.md §"Patterns to flag in summaries / reviews"](../dataverse-classic-workflow/reference/trigger-types.md#patterns-to-flag-in-summaries--reviews).128Report any matches as a "Findings" section at the bottom of the summary.129130---131132## Output template133134````135# {Workflow Display Name}136137**Primary entity:** `{logical_name}`138**Category:** Classic Workflow139**Mode:** Background | Real-Time140**State:** Draft | Activated141142## Trigger143- Trigger types: {OnCreate, OnUpdate, …}144- Filtering attributes (for OnUpdate): {list, or "(none — fires on every update)"}145- Scope: {User | BusinessUnit | ParentChildBU | Organization}146- Run as: {Owner | CallingUser}147148## Steps1491501. **{Step display name}** — {plain-English description}151 - {sub-detail like "Set name to {Account: Name} & ' (reviewed)'"}1522. **Check Condition: {description}**153 - **If True:**154 1. {nested step}155 2. {nested step}156 - **Else:**157 1. {nested step}1583. **Stop Workflow** — Status: Canceled. Reason: "{message}"159160## Findings161162- ⚠️ {finding} ({severity})163- …164165(Or "No issues detected." if the workflow looks clean.)166````167168---169170## Error handling171172- **File not found** → ask the user to confirm the path. Suggest running173 `pac solution unpack` if they haven't.174- **Invalid XAML** (parse error) → report the parse error verbatim. Do175 not try to "fix" it silently. Ask the user to share the error message176 with the source they exported from.177- **Wrong category** (BPF, Business Rule, Modern Flow) → as in Step 1,178 stop and explain.179- **Activity types you don't recognize** → look them up in180 [reference/activity-types.md](../dataverse-classic-workflow/reference/activity-types.md). If181 still unknown, treat them as Custom CodeActivity (Section 8 of that182 reference) and surface the AssemblyQualifiedName.183184---185186## Don't187188- Don't emit XAML in your summary unless the user asks for it. The point189 of this skill is human-readable output.190- Don't echo the user's real entity logical names back into a generated191 example. References in prose are fine; synthetic XAML examples must use192 placeholders.193- Don't silently drop activity types. If you collapse a helper, mention194 it ("Includes a checkpoint after step 3"). If you skip something195 unrecognized, say so.