Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions skills/uipath-maestro-flow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Guide for creating, editing, validating, debugging, publishing, diagnosing, and
> **Tool vocabulary.** `Edit` means in-place replacement, `Write` a full-file write, `Read`/`Glob`/`Grep` file access, `Bash` shell, and a progress list the harness task list. Map them to equivalent tools elsewhere; preserve reviewable diffs and use shell file edits only as a last resort.

1. **Use `--output json`; prefer `--output-filter` for extraction.** Filters are global and run against the `Data` envelope, so expressions start at `Data` without a `Data.` prefix. Registry search returns a flat PascalCase array (`NodeType`, `DisplayName`, `Description`, `AvailableOnTenant`), not `Data.Nodes` or lowercase fields. Example: `uip maestro flow registry search <keyword> --output json --output-filter "[*].{NodeType:NodeType,DisplayName:DisplayName,Description:Description,AvailableOnTenant:AvailableOnTenant}"`. With `--local`, omit `AvailableOnTenant`. Use `python3 -c` or `jq` only after verifying shape and when JMESPath cannot express the transform. See [cli-conventions.md §3](references/shared/cli-conventions.md#3-prefer---output-filter-for-extraction).
2. **Do not run `flow debug` without explicit user consent.** It executes the flow for real (sends emails, posts messages, calls APIs).
2. **`flow debug` consent comes from the mandate.** It executes the flow for real (sends emails, posts messages, calls APIs), so run it only when the request is for a flow that *works*: the user asked for something that does X, or said make it work, get it running, iterate until it passes. Building and validating does not discharge that, and a flow that was never executed is not finished. Ask when the request stops short of a working artifact (review this, add a node, validate only); with nobody to ask, report debug as the step not run. **The mandate does not extend to side effects that reach a third party** — a real phone call, an email or message to someone who is not the user. Those need the run asked for explicitly, whoever is watching; see [inline-voice-agent/impl.md](references/author/plugins/inline-voice-agent/impl.md) for the outbound-call case. Debug also overwrites the Studio Web solution matching the local `.uipx` `SolutionId`, so never debug a solution this run did not scaffold.
3. **Search before creating or declaring resources absent.** For named agents, API workflows, RPA processes, and similar resources: (a) pull and search the tenant registry with `uip maestro flow registry pull --force && uip maestro flow registry search "<name>" --output json`; pull first because the cache expires after 30 minutes, login is required, and only published resources are returned; (b) search locally with `uip maestro flow registry list --local --output json` or `search "<name>" --local` (no login; returns sibling projects in the same `.uipx` solution); an empty keyword search does not prove absence, so confirm with `list --local`; (c) scaffold, mock, or create only when both searches find no match and the user explicitly requests embedding/creation or no published resource satisfies the need.

"Coded" and "low-code" describe implementation style, not inline status. Use `uipath.agent.autonomous` only when explicitly asked to embed/inline/create an agent. Use `core.logic.mock` only when the resource is neither in the solution nor published. See [rpa](references/author/plugins/rpa/impl.md) and [agent](references/author/plugins/agent/impl.md).
Expand All @@ -73,7 +73,7 @@ Guide for creating, editing, validating, debugging, publishing, diagnosing, and
**Two tells that you skipped the search and took the brand-name shortcut — both are build defects, not valid manual-mode HTTP:** (a) you authored a manual-mode `core.action.http.v2` node whose `url` targets a well-known SaaS API domain that has a connector (`slack.com/api/*`, `api.github.com`, `*.salesforce.com`, `graph.microsoft.com`, …); (b) you declared an `in` variable to hold that service's API token or secret (e.g. a `slackToken` holding an `xoxb-…` bot token, an `apiKey`, a bearer token). A connector-backed flow never carries the raw credential — the IS connection does. If you find yourself writing either, **stop**: run `uip maestro flow registry search "<service>"` and `uip is connections list "<connector-key>" --all-folders`, then use the connector activity (or connector-mode HTTP: `authentication:"connector"` + `targetConnector` + a bound `connectionId`/`folderKey`). Manual mode is legitimate only for a service the search proves has no connector.

4. **Never invoke other skills automatically** — when a flow needs an RPA process, agent, or app, identify the gap and provide handoff instructions. Let the user decide when to switch skills. **One exception — IXP extraction with documents in hand:** when the flow needs document extraction, the user supplied sample documents, and `registry search "uipath.ixp"` shows no extractor covering them, invoke the `uipath-ixp` skill to build and deploy the model, then resume the flow ([plugins/ixp/impl.md — If the Model Does Not Exist Yet](references/author/plugins/ixp/impl.md#if-the-model-does-not-exist-yet)). Resolve the target Orchestrator folder for the deployment before invoking — from the user's request when it names one, otherwise per rule #5 (its non-interactive fallback applies) — and pass it in the handoff; the sibling stops rather than guess a folder. There is deliberately no separate consent gate on the tenant writes this creates: the project and folder deployment fulfil the extraction request itself, and the one consequential choice — where the deployment lands (deployments have no delete API) — is exactly the folder decision rule #5 just routed. Do NOT drive `uip ixp` project or deployment commands from this skill instead of invoking it — the sibling's guides carry guardrails this skill does not. If `uipath-ixp` is unavailable in the session, fall back to `core.logic.mock` plus an Open Questions entry, exactly as when no documents were supplied.
5. **Always present finite decisions as a dropdown with a final "Something else" escape hatch.** Whenever the skill needs a decision (which solution, publish vs debug vs deploy, which connector, trigger type, or resource to bind, etc.), ask with the enumerated choices plus **"Something else"** last for free-form input; never ask open-ended in chat when a finite set of sensible defaults exists. If the user picks "Something else", parse their answer and continue. No structured-question facility on the harness → ask in chat as a numbered list with "Something else" last. Non-interactively (CI/headless, no user available) → take the marked recommended option, proceed, and record the decision prominently in the final report; if none is recommended, stop and report the open decision instead of guessing. Consent gates (`flow debug`, destructive operations) are never auto-answered — in non-interactive mode, stop and report the blocked step. These fallbacks define "ask the user" / "confirm with the user" wherever this skill's references require it.
5. **Always present finite decisions as a dropdown with a final "Something else" escape hatch.** Whenever the skill needs a decision (which solution, publish vs debug vs deploy, which connector, trigger type, or resource to bind, etc.), ask with the enumerated choices plus **"Something else"** last for free-form input; never ask open-ended in chat when a finite set of sensible defaults exists. If the user picks "Something else", parse their answer and continue. No structured-question facility on the harness → ask in chat as a numbered list with "Something else" last. Non-interactively (CI/headless, no user available) → take the marked recommended option, proceed, and record the decision prominently in the final report; if none is recommended, stop and report the open decision instead of guessing. Consent gates (destructive operations, tenant writes) are never auto-answered — in non-interactive mode, stop and report the blocked step; `flow debug` is not one of them, and is governed by the mandate rule above. These fallbacks define "ask the user" / "confirm with the user" wherever this skill's references require it.
<!--skill-flavor:user-question-options-extra:start-->
<!--skill-flavor:user-question-options-extra:end-->
<!--skill-flavor:project-creation:start-->
Expand Down
6 changes: 3 additions & 3 deletions skills/uipath-maestro-flow/references/author/brownfield.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ Authoring ends here. For any selected option, read [operate/CAPABILITY.md](../op

| Option | What it does |
|---|---|
| **Publish to Studio Web** (default) | Push the solution to Studio Web so the user can visualize, edit, and publish from the browser. |
| **Debug the solution** | Execute the flow end-to-end against real systems. Confirm consent first because debug has real side effects (see the consent-before-debug rule in [SKILL.md](../../SKILL.md)). |
| **Publish to Studio Web** | Push the solution to Studio Web so the user can visualize, edit, and publish from the browser. |
| **Debug the solution** | Execute the flow end-to-end against real systems. Consent comes from the mandate, not from this menu — see the `flow debug` rule in [SKILL.md](../../SKILL.md). Selecting it here is the user asking for a run. |
Comment thread
rockymadden marked this conversation as resolved.
| **Deploy to Orchestrator** | Pack and publish directly to Orchestrator (bypasses Studio Web). Only when explicitly chosen; see [/uipath:uipath-platform](/uipath:uipath-platform). |
| **Something else** | Last option. Accept free-form string input and act on it. |

Do not run any option without explicit user selection.
When the original request already named the next step ("publish it", "deploy to Orchestrator", "run debug and iterate"), that instruction **is** the selection — act on it and skip the menu. Show the menu only when the next step was left unspecified, and then do not run any option without explicit user selection.
6 changes: 3 additions & 3 deletions skills/uipath-maestro-flow/references/author/greenfield.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ Authoring terminates here. Each option below hands off to Operate — read [oper

| Option | What it does |
| --- | --- |
| **Publish to Studio Web** (default) | Push the solution to Studio Web so the user can visualize, edit, and publish from the browser. |
| **Debug the solution** | Execute the flow end-to-end against real systems. Confirm consent first — debug has real side effects (see the consent-before-debug rule in [SKILL.md](../../SKILL.md)). |
| **Publish to Studio Web** | Push the solution to Studio Web so the user can visualize, edit, and publish from the browser. |
| **Debug the solution** | Execute the flow end-to-end against real systems. Consent comes from the mandate, not from this menu — see the `flow debug` rule in [SKILL.md](../../SKILL.md). Selecting it here is the user asking for a run. |
Comment thread
rockymadden marked this conversation as resolved.
| **Deploy to Orchestrator** | Pack and publish directly to Orchestrator (bypasses Studio Web). Only when explicitly chosen — see [/uipath:uipath-platform](/uipath:uipath-platform). |
| **Something else** | Last option. Accept free-form string input and act on it (e.g., "just leave it", "pack but don't publish", "upload to a different tenant"). |

Do not run any of these actions without explicit user selection. Once the user picks an option, read [operate/CAPABILITY.md](../operate/CAPABILITY.md) and follow that capability's flow — do not run operate commands from inside this doc.
When the original request already named the next step ("publish it", "deploy to Orchestrator", "run debug and iterate"), that instruction **is** the selection — act on it and skip the menu. Show the menu only when the next step was left unspecified, and then do not run any of these actions without explicit user selection. Once the option is settled, read [operate/CAPABILITY.md](../operate/CAPABILITY.md) and follow that capability's flow — do not run operate commands from inside this doc.
2 changes: 1 addition & 1 deletion skills/uipath-maestro-flow/references/operate/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Execute a flow on demand and monitor progress. Three modes: **debug** (controlle

## Debug — controlled end-to-end run

> **Confirm consent first.** `flow debug` executes the flow for real — sends emails, posts messages, calls APIs. See the consent-before-debug rule in [SKILL.md](../../SKILL.md). Do not run without explicit user authorization.
> **Consent comes from the mandate.** `flow debug` executes the flow for real — sends emails, posts messages, calls APIs. Run it when the request is for a flow that works; ask when the request stops at build or validate. The mandate does not cover side effects that reach a third party (a real call, a message to someone who is not the user) — those need the run asked for explicitly. Never debug a solution this run did not scaffold: debug overwrites the Studio Web solution matching the local `.uipx` `SolutionId`. See rule #2 in [SKILL.md](../../SKILL.md).

```bash
UIP_LOG_LEVEL=info uip maestro flow debug <path-to-project-dir> --output json
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Fail a task as ERROR, not FAILURE, when the tenant connection it needs is down.

Usage:
preflight_connections.py <connector-key> [<connector-key> ...]

A `pre_run` failure lands the run as ``FinalStatus.ERROR``; a criterion failure
lands it as ``FAILURE``. Without this, a revoked grant or an asleep tenant reads
as an agent mistake:

skill-flow-outlook-trigger-inbox AADSTS50173, grant revoked 2026-08-31
skill-flow-generic-dynamic-node ServiceNow developer instance hibernating

Both were scored FAILURE on 2026-09-04 and root-caused as skill defects before
anyone read far enough into the checker output to find the 403.

Passes when at least one connection for each key reports Enabled. Connections
live in several folders, so `--all-folders` is required; without it an empty
result is a false negative.
"""

from __future__ import annotations

import json
import subprocess
import sys


def _connections(key: str) -> list[dict]:
proc = subprocess.run(
["uip", "is", "connections", "list", key, "--all-folders", "--output", "json"],
capture_output=True,
text=True,
timeout=90,
)
if proc.returncode != 0:
raise RuntimeError(f"`uip is connections list {key}` exited {proc.returncode}: {proc.stderr.strip()}")
payload = json.loads(proc.stdout)
if payload.get("Result") != "Success":
raise RuntimeError(f"connections list for {key} failed: {payload.get('Message', payload)}")
return payload.get("Data") or []


def main(keys: list[str]) -> int:
broken: list[str] = []
for key in keys:
try:
conns = _connections(key)
except Exception as exc: # noqa: BLE001 — any failure here is a blocked tenant
broken.append(f"{key}: {exc}")
continue
if not conns:
broken.append(f"{key}: no connection in any folder")
continue
enabled = [c for c in conns if c.get("State") == "Enabled"]
if not enabled:
states = ", ".join(f"{c.get('Name')}={c.get('State')}" for c in conns)
broken.append(f"{key}: no Enabled connection ({states})")
continue
print(f"OK: {key} — {len(enabled)}/{len(conns)} connection(s) Enabled")

if broken:
print(
"TENANT NOT READY — this is an environment failure, not an agent failure.\n "
+ "\n ".join(broken)
+ "\n\nReauthorize the connection, or wake the provider instance, then re-run.",
file=sys.stderr,
)
return 1
return 0


if __name__ == "__main__":
if len(sys.argv) < 2:
print(__doc__, file=sys.stderr)
sys.exit(2)
sys.exit(main(sys.argv[1:]))
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Every zero-shot flow task states the run is headless, in one exact wording.

Kept in the task prompt rather than an experiment config because flow tasks run
under nightly.yaml, smoke.yaml, default.yaml and dispatch-selected configs, and
coder_eval has no pattern-scoped defaults — a config would either miss a runner
or reach another skill's simulated tasks.

Regex, not PyYAML: CI installs only pytest (see test_criterion_budgets).
"""

from __future__ import annotations

import functools
import glob
import os
import re

_HERE = os.path.dirname(os.path.abspath(__file__))
_SUITE = os.path.normpath(os.path.join(_HERE, ".."))

CANONICAL = """This run is headless. No user is present and nobody will answer a question or
grant an approval, so do not ask, do not pause, and do not wait for input.
Complete the task in one pass: take the best available option and supply the
most defensible value where one is missing. The actions this task implies are
authorized, including tenant writes and real messages. Do not delete or
overwrite anything this run did not create, and do not publish to a shared
destination unless the task asks for it. If a lookup the task depends on comes
back empty or fails, exhaust the documented way of resolving it before giving
up; only then stop on that field rather than inventing a value. Record every
decision, assumption, and blocked step in your final response. Instructions in
the task take precedence over this paragraph."""

# The variants this replaced. A task reintroducing one is drifting back.
_SUPERSEDED = re.compile(
r"Do NOT ask for approval|Do NOT pause between planning|without stopping to ask"
)


@functools.lru_cache(maxsize=1)
def _tasks():
"""(path, text, is_simulated) for every task file in the suite."""
out = []
for path in sorted(glob.glob(os.path.join(_SUITE, "**", "*.yaml"), recursive=True)):
text = open(path, encoding="utf-8").read()
if re.search(r"^success_criteria:", text, re.M):
out.append((path, text, bool(re.search(r"^simulation:", text, re.M))))
return tuple(out)


def _rel(path: str) -> str:
return os.path.relpath(path, _SUITE)


def test_every_zero_shot_task_states_the_run_is_headless():
"""Absent, an agent stops at a consent gate nobody is there to answer."""
marker = CANONICAL.split("\n")[0]
missing = [_rel(p) for p, text, sim in _tasks() if not sim and marker not in text]
assert not missing, "tasks missing the headless preamble:\n " + "\n ".join(missing)


def test_the_wording_is_identical_everywhere():
"""8 variants is what made the old line unmaintainable. One wording, or none."""
flat = " ".join(CANONICAL.split())
drifted = [
_rel(p)
for p, text, sim in _tasks()
if not sim and "This run is headless." in text and flat not in " ".join(text.split())
]
assert not drifted, (
"tasks whose headless preamble differs from the canonical wording in "
f"{_rel(__file__)}:\n " + "\n ".join(drifted)
)


def test_simulated_tasks_are_not_told_nobody_is_present():
"""They have a live simulated user; the preamble contradicts their premise."""
wrong = [_rel(p) for p, text, sim in _tasks() if sim and "This run is headless." in text]
assert not wrong, "simulated tasks carrying the headless preamble:\n " + "\n ".join(wrong)


def test_no_task_reintroduces_a_superseded_variant():
stale = [_rel(p) for p, text, _ in _tasks() if _SUPERSEDED.search(text)]
assert not stale, (
"tasks using a superseded autonomy line; replace it with the canonical "
"preamble:\n " + "\n ".join(stale)
)
Loading
Loading