Build a Workflow With a Code Assistant

Scenario: Create a workflow for a salesperson to manage their opportunities.
Note: You can use the LLM to generate the prompt for you.
Here's the prompt that was generated by a code assistant.
Build a **standalone AI Studio workflow** that manages **Opportunities** based on a free-text question the customer provides when the workflow runs.

## 1. What the workflow must do

The customer supplies a single input **message/question** at run time. Read it from `$context.$system.$inputMessage` (treat that as the question). The workflow must:

1. **Classify intent** from the question. Supported intents (Opportunity management only):
    - `LIST` — show **all** of the user's opportunities, with no filter criteria (e.g. "show all opportunities").
    - `FILTERED_LIST` — show a **filtered subset** of the user's opportunities based on criteria expressed in the question (e.g. "show opportunities with a win probability of 70 or higher", "show opportunities with revenue > 100000"). The criteria can reference any queryable opportunity field with a comparison (>=, >, <=, <, =, between, contains, etc.) and may combine multiple conditions.
    - `UPDATE` — update an existing opportunity. This includes updating any updateable field(s) of the opportunity **and/or** creating opportunity **revenue** records against it.
    - `CREATE` — create a new opportunity, optionally **with** revenue records.
    - `OUT_OF_SCOPE` — anything that is not opportunity management.
2. **Guard scope.** If the intent is `OUT_OF_SCOPE`, do not attempt any operation. Return a friendly message that states the purpose of the workflow: it only helps the user view, create, and update their Sales Opportunities (including opportunity revenue records), and invites them to rephrase. Do not call any business object on this branch.
3. **Perform the operation** matching the detected intent using the Opportunity business object function(s).
4. **Return a user-friendly response.** The final user-facing text must be natural-language, confirm what was done (e.g. fields updated, opportunity created with its key/ID, revenue lines added), and for `LIST` / `FILTERED_LIST` summarize the opportunities readably (for `FILTERED_LIST`, restate the filter that was applied and how many matched). Ground the response in real upstream node output — do not fabricate IDs, fields, or results.

## 2. Business Object identification (do this before wiring operation nodes)

We need to **find, create, and update opportunities**, plus create **opportunity revenue** records. Reuse before you build:

1. Use `search-business-objects` to find an existing Opportunity business object and inspect its functions (query/list, create, update, and revenue-record creation). Prefer an existing reusable BO.
2. **Verify it meets the requirement** — it must expose functions to: query the user's opportunities, update updateable opportunity fields, create an opportunity, and create opportunity revenue records (a child/revenue function or a create that accepts revenue lines).
3. **If a suitable BO exists, use it.** Carry the selected `search-business-objects` result forward as `businessObjectHint` on every later BO tool call (`get-business-object-functions`, `do-create-node`, `do-modify-node`, etc.).
4. **If no existing BO meets the need**, create a new Business Object source artifact (`.bo`) for Opportunities (and its revenue records) per the `business-object-builder.md` reference, then use it.

Do not guess `businessObjectCode` or `functionName`; select the real BO and the real functions. Let `do-create-node` / `do-modify-node` resolve and persist each `BO_FUNCTION` node's `outputSpecification` (pass `businessObjectHint`, omit `outputSpecification`), then reference only fields that actually exist in the resolved schema.

## 3. Suggested topology (adapt to the real BO functions)

START
  -> CLASSIFY_INTENT (LLM)  // outputs intent token + extracted filter criteria as JSON
  -> SWITCH on the classified intent
       case LIST          -> LIST_OPPORTUNITIES (BO_FUNCTION: query, no filter)         -> FORMAT_LIST (LLM)     -> END
       case FILTERED_LIST -> BUILD_FILTER (CODE) -> QUERY_OPPORTUNITIES (BO_FUNCTION: query with filter) -> FORMAT_FILTERED (LLM) -> END
       case CREATE        -> CREATE_OPPORTUNITY (BO_FUNCTION: create)
                           -> [if revenue requested] CREATE_REVENUE (BO_FUNCTION)  -> FORMAT_CREATE (LLM)  -> END
       case UPDATE      -> [resolve target opportunity]
                           -> UPDATE_OPPORTUNITY (BO_FUNCTION: update updateable fields)
                           -> [if revenue requested] CREATE_REVENUE (BO_FUNCTION)  -> FORMAT_UPDATE (LLM)  -> END
       case OUT_OF_SCOPE -> RETURN (static purpose message)

Guidance for wiring:
- Use a **SWITCH** node whose `caseExpression` reads the intent produced by `CLASSIFY_INTENT`. Give each case its own dedicated path; do not collapse multiple intents into one terminal prompt.
- The `CLASSIFY_INTENT` LLM prompt must inject the question explicitly (`{{$context.$system.$inputMessage}}`). It must output a single intent token (`LIST` | `FILTERED_LIST` | `UPDATE` | `CREATE` | `OUT_OF_SCOPE`) plus, for `FILTERED_LIST`, a structured list of the filter criteria the user expressed — each as field + operator + value (e.g. `[{"field":"WinProbability","op":">=","value":70}]`). Give the node an `outputSpecification` that declares both the intent token and the criteria array so the SWITCH and the filter-builder can read them. Have the SWITCH match on the intent token field only.
- **`FILTERED_LIST` path:** the `BUILD_FILTER` CODE node converts the extracted criteria into whatever the chosen query function actually expects (e.g. an `RSQL`/`q` filter string, a `where`/`finder` parameter, or named bind parameters — match the real BO function signature). Inspect the query function's token parameters via `get-business-object-functions` and build the filter to fit. Then bind `QUERY_OPPORTUNITIES`' filter input to `{{$context.$nodes.BUILD_FILTER.$output.result.<filterField>}}`. If the chosen query function cannot accept server-side filters, fetch the user's opportunities and apply the criteria inside `BUILD_FILTER` (or a post-query CODE node) instead — but prefer server-side filtering when supported. Only reference opportunity field names that exist in the BO function's resolved `outputSpecification`/parameter set; do not invent field names like `WinProbability` or `Revenue` without confirming the real ones.
- For `UPDATE` and revenue creation, extract the needed parameters (target opportunity identifier, the fields to change, revenue line details) from the question. If the BO update/create functions need a `personId`/`assignmentId` or the current user's identity, insert the standard upstream identity BO function (`Logged In User Assignment Info` / `fetch_loggedIn_user_assignmentId`) and bind those IDs before the operation node.
- Add **CONDITION guards** before any node that depends on a first record (e.g. `...$output.items[0].Id`) and route empty-data cases to a graceful no-data message instead of calling a downstream BO with missing inputs.
- Only run the revenue-creation node when the question actually asks for revenue records (branch it behind an IF/CONDITION); otherwise skip straight to the formatting node.
- Each formatting LLM node must inject the relevant upstream BO output expression (`{{$context.$nodes.<NODE>.$output...}}`) and produce concise, friendly, accurate confirmation/summary text.

## 4. Constraints & reminders

- Expressions are `{{...}}` with no inner spaces. No template block tags (`{{#if}}`, `{{#each}}`, etc.) — use control-flow nodes for conditional behavior.
- `CODE` node business fields are read under `$output.result...`.
- Normalize `workflowCode` to uppercase with underscores (e.g. `OPPORTUNITY_MANAGEMENT`). Pick the workflow family/product from the real LOV options for the Sales/Opportunity domain; if there is no exact match, ask before guessing.
- Keep every node fully wired into the execution path in the same pass — no floating nodes.

## 5. Finish criteria (do not stop early)

After the structural batch:

1. Run `do-prettify-workflow --file <workflow-file>`.
2. Run `validate-workflow --file <workflow-file>` and fix anything it reports.
3. Since this is a newly created workflow, continue into the workflow **test sync** loop: start with `get-workflow-test-sync-plan --file <workflow-file>` (per `workflow-test-authoring.md`) and run the tests. Do not use remote judging unless explicitly asked.
4. Report the final suite/package summary, preserving the `Metrics:` and `Optimization:` lines.

If anything about the Opportunity BO functions or their parameters is genuinely ambiguous after searching, ask one targeted question rather than inventing schema.

Instructions given to the code assistant

Here are the instructions given to a code assistant to generate the above prompt. You can review the output and confirm that the prompt has instructions to what you want as the output before it generates all the artifacts.
Use the /aistudio  skill to create a prompt that I can use in a code assistant to achieve the below:
Use Case
* Create a workflow that can do the following based on the question asked by the customer.
   * See all the users opportunities
   * Update an opportunity
      * The user may want to update any updateable fields of the opportunity
      * The user may want to create opportunity revenue records
   * Create an opportunity
      * Create an opportunity with revenue records
* The user would provide an input message or a question when running the workflow.
* Use the question to figure out what the customer wants to do. We should only support management for opportunities. If the question is for something other area then respond back with what the purpose of the workflow is.
* Once you identify what the customer wants to do (intent of the user), perform the appropriate operation and return the output to the user in a user friendly way.
Business Object Identification
For this workflow, we need to find, create and update opportunities. You need business objects for that.

* Find existing business object to query opportunities, update opportunities and create opportunity
* Make sure that business object meets our requirement
* If it meets the requirements, use the business object. If it does not meet the need, create a new business object
  
The openAPI describe for the opportunity object has been added to the project folder.