Skill Resolution Standards

Overview

Every skill in MelodyArc must conclude with an entity controller — the point responsible for resolving the current skill entity and activating the next one when appropriate.
The entity controller forms the bridge between a skill’s execution logic and the operator’s state management, ensuring that progress, messages, and state updates are handled in a consistent, reliable way.


1️⃣ Purpose of an Entity Controller

The entity controller is the final step in a skill’s lifecycle. Its job is to:

  • Resolve the current skill entity (mark it as completed or “done”).
  • Activate the next entity in the sequence (mark it as “ready” when applicable).
  • Update operator state with any information produced or transformed by the skill.
  • Send user-facing messages through the MelodyArc Chat Service (MCS) when required.

This keeps each skill self-contained while allowing the operator to progress smoothly from one skill to the next.


2️⃣ Resolution Responsibilities

When a skill resolves, its entity controller should:

  1. Mark the entity as complete

    • Set the current entity’s status to "done".
  2. Activate the next skill (if applicable)

    • Locate the next entity with status: "waiting" and mark it "ready".
  3. Update the operator state

    • Add or modify relevant key/value pairs in operator_state.
    • Use prefix-based key mapping defined in the saveStates object (see below).
  4. Send outgoing messages to the user via the MelodyArc Chat Service

    • Use sendMcsUpdate to provide confirmation, results, or contextual messages to the user via MCS.
  5. Return a structured object that merges these results for the next execution stage.


3️⃣ Using the generic_controller

In most cases, you do not need to write a custom controller.
The standard generic_controller is designed to handle the full lifecycle for 90% of skills.

What the generic_controller handles:

  • Resolves the current entity (status: "done").
  • Prepares and activates the next skill entity (status: "ready").
  • Sends messages to the user through _mcs.appendMessage().
  • Updates operator state using mappings defined in saveStates.
  • Returns a fully structured output for downstream processing.

When to use it:

Use the generic controller unless:

  • The skill needs custom sequencing or branching logic.
  • You must process or format complex data before sending a user message.
  • You need to trigger non-standard integrations beyond MCS and state update behavior.

If one of those conditions applies, you can use the generic controller’s logic as a base and extend it.


4️⃣ Key Configuration Parameters

Both saveStates and sendMcsUpdate are defined as attributes of the invoke that calls the generic_controller.
They provide declarative control over how the controller updates state and communicates with the user.
Additional configuration keys may be introduced in the future to support expanded behaviors and advanced resolution logic.


🗂️ saveStates

The saveStates object defines how to map values from the current entity into the operator state.
Keys are prefix mappings: any field in _this (the current entity) that begins with the prefix will be copied into operator_state under the new destination path.

Example:

"saveStates": {
  "_this.result.": "operator_state.skill_results.",
  "_this.formData.": "operator_state.last_submission."
}

This allows the controller to dynamically persist relevant data between skills without hardcoding field paths.


💬 sendMcsUpdate

sendMcsUpdate defines the message that should be sent to the user at the end of the skill resolution step.
It can be defined either:

  • At the entity level (_this.sendMcsUpdate), or
  • In the invoke context (_token.sendMcsUpdate).

The controller uses the first available source and automatically expands dynamic placeholders through the fill_text function before sending the message via MCS.

Example:

"sendMcsUpdate": "Your request for [[_this.formData.item_name]] has been submitted successfully."

5️⃣ Data Contexts

ContextSourceDescription
_thisEntity dataContains the live skill entity being resolved (fields, status, message templates, etc.).
_tokenInvocation contextHolds task-level and operator-level parameters injected when the controller runs (conversation details, metadata, integration name, etc.).

6️⃣ Example Resolution Flow (Generic Controller)

  1. Controller starts

    • Reads entity type (_this._entity.type).
    • Loads entity list from _token[entityType].
  2. Finds the current and next entities

    • Marks the current entity as "done".
    • Marks the next waiting entity as "ready", if one exists.
  3. Sends user message

    • Expands sendMcsUpdate text with fill_text().
    • Sends it through _mcs.appendMessage() with proper sender and metadata.
  4. Updates operator state

    • Reads _token.saveStates and maps _this values accordingly.
  5. Returns structured output

    • Includes entity status changes, updated operator state, and any message results.

9️⃣ Best Practices

✅ Prefer the generic_controller whenever possible — it handles 90% of use cases.
✅ Use saveStates for clean, declarative operator state mapping.
✅ Always include a sendMcsUpdate message when user confirmation or summary is expected.
✅ Keep skill data lightweight — avoid deeply nested objects that complicate key mapping.
✅ Define new configuration keys thoughtfully and document them in future revisions of this standard.


🧠 Summary

An entity controller is how a skill finalizes itself.
It:

  • Resolves the current skill,
  • Activates the next,
  • Updates operator state,
  • And communicates with the user.

Most of the time, you can achieve this simply by using the generic controller, configured through an invoke using the attributes saveStates and sendMcsUpdate.
This ensures all skills resolve consistently, share data cleanly, and maintain synchronized state across the operator workflow.


Maintained by: MelodyArc Platform Engineering
Applies to: All skill entity controllers
Version: 1.1