diff --git a/skills/uipath-process-mining/SKILL.md b/skills/uipath-process-mining/SKILL.md index 385ffc9afa..a813f04af2 100644 --- a/skills/uipath-process-mining/SKILL.md +++ b/skills/uipath-process-mining/SKILL.md @@ -1,7 +1,7 @@ --- name: uipath-process-mining -description: "UiPath Process Mining via `uip pm` — build and operate a process app end-to-end from a CSV / event log: discover templates, create an app with a data mapping, upload, ingest, author the dbt (Snowflake) transformation layer, publish, and query it (group-by/metrics, details, percentiles, RCA, insights). Covers the `uipath.custom` template, the `Cases.sql` optional-column gotcha, exposing custom tables as Case-linked data-model tables (add-table + re-ingest), the `query run --group-by/--metric` sugar, `--wait` on async ingest/transform, the apply-not-reingest fix loop, and fixing a wrong mapping in place via `apps data-mapping get|update` (no app rebuild). For Orchestrator / Data Fabric / Integration Service→uipath-platform. For `.flow`/Maestro→uipath-maestro-flow. For IXP document models→uipath-ixp." -when_to_use: "User mentions process mining, a process app, an event log, `uip pm`, mining a CSV/log, ingesting data into a process app, dbt/SQL transformations of one, steps-to-resolution / throughput / variant / rework analysis, or querying a process app. Also 'build a process app from this data', 'ingest this log', 'fix my Cases.sql', 'why can't I query my custom table', 'add a table to the data model', 'group by X average Y in process mining', 'fix/change/read my data mapping', 'wrong date format in the mapping', 'must I recreate the app to change the mapping'. For Orchestrator/queues/Data Fabric→uipath-platform; `.flow`→uipath-maestro-flow; IXP/Document Understanding→uipath-ixp." +description: "UiPath Process Mining via `uip pm` — build and operate a process app end-to-end from a CSV / event log: templates, data mapping, upload, ingest, the dbt (Snowflake) transformation layer, publish, and query it (metrics, percentiles, RCA). Covers `uipath.custom`, the `Cases.sql` optional-column gotcha, Case-linked data-model tables (add-table + re-ingest), the apply-not-reingest fix loop, fixing a wrong mapping in place via `apps data-mapping get|update` (no app rebuild), and editing the app model via `apps model fields` — a field's data kind / calculated fields, including the numeric→duration mismatch that locks dashboards open (DNA-46960). For Orchestrator/Data Fabric/Integration Service→uipath-platform. For `.flow`/Maestro→uipath-maestro-flow. For IXP→uipath-ixp." +when_to_use: "User mentions process mining, a process app, an event log, `uip pm`, mining a CSV/log, ingesting data into one, dbt/SQL transformations, steps-to-resolution / throughput / variant / rework analysis, or querying one. Also 'build a process app from this data', 'ingest this log', 'fix my Cases.sql', 'why can't I query my custom table', 'add a table to the data model', 'group by X average Y', 'fix/change/read my data mapping', 'wrong date format in the mapping', 'change a field's data kind', 'set a field to duration', 'add a calculated field/metric', 'my dashboards won't open', 'Must be duration not numeric (DNA-46960)'. For Orchestrator/Data Fabric→uipath-platform; `.flow`→uipath-maestro-flow; IXP→uipath-ixp." allowed-tools: Bash, Read, Write, Glob, Grep --- @@ -11,7 +11,12 @@ Build and operate a UiPath Process Mining process app end-to-end from the termin **This works for every app type**, not just `uipath.custom`: the pipeline (mapping → upload → ingest → transform → data model → query) is identical across the `uipath.custom` event-log template and the source-system templates (P2P / O2C / IM / AP / … on SAP, Oracle, NetSuite, ServiceNow, Salesforce, …). Only **what the data mapping / extract must contain** differs. See [`references/app-types.md`](references/app-types.md). -The command groups: `uip pm app-types` (list/get — templates), `apps` (list/create/delete/publish, `model get|add-table`, `data-mapping get|update`), `files` (upload), `ingestions` (create/logs), `transformations` (list/get/create/update/apply/run/status/logs — the dbt dev loop), and `query` (run/details/percentile/rca/insights/info/layout). +This skill is the **process-mining domain layer** — *what* to build and *why*. The +low-level mechanics of driving the tool — the command-group map, the `Result`/`Code`/`Data` +output envelope, the ETag get-modify-put pattern, `--wait`, `--stage`, and field-id +discovery — are one layer down in [`references/uip-pm-cli.md`](references/uip-pm-cli.md). +The rules below carry the headline command and link down to it and to the domain +references for the full detail. ## When to Use This Skill @@ -19,77 +24,40 @@ The command groups: `uip pm app-types` (list/get — templates), `apps` (list/cr - **Author the transformation layer** — edit the dbt (Snowflake) SQL models that produce the process model, then re-run. - **Query a process app** — pull numbers out: aggregate group-by + metrics, raw detail rows, percentiles, root-cause analysis, process insights. - **Expose custom analysis** — surface your own analytical table (a weekly aggregate, an impact study) as a queryable entity. +- **Edit the app model** — change a field's data kind, add calculated fields, or fix a data-kind mismatch that locks dashboards open (DNA-46960). - **Manage the app lifecycle** — stages (dev → published), RBAC, deletion. -## App lifecycle & the ELT editor +## App lifecycle -An app moves through: **create** (from a template + data mapping) → **load** (`files upload` + `ingestions create`) → **transform** on the **dev** stage (the ELT editor — the dbt model tree) → **publish** to the **published** stage → **query** / build dashboards. `--stage dev|published` selects the stage on every data/transform/query command (default `dev`). - -The **ELT editor** is the `transformations` command group operating on the dbt (Snowflake) model tree — the extract-load-transform layer that turns loaded source tables into the process model: - -| Operation | Command | -|-----------|---------| -| List the model tree | `transformations list ` | -| Read a file (or save locally) | `transformations get [--destination ]` | -| Edit an existing file (ETag-safe) | `transformations update --file ` | -| Create a new model file | `transformations create --file ` | -| Re-run the **full** transform on loaded data | `transformations apply --wait` | -| Rebuild **one** dev model + dependents | `transformations run --model models/X.sql` | -| Status / logs of the last build | `transformations status ` · `transformations logs ` | +An app moves through: **create** (from a template + data mapping) → **load** (upload + ingest) → **transform** on the **dev** stage (the ELT/dbt layer) → **publish** to the **published** stage → **query** / build dashboards. Develop against a small subset on `dev`, then publish the full dataset for real analysis ([`references/lifecycle-and-rbac.md`](references/lifecycle-and-rbac.md)). The **ELT editor** is the `transformations` command group over the dbt (Snowflake) model tree that turns loaded source tables into the process model — its command surface and the apply-vs-run distinction are in [`references/transformations.md`](references/transformations.md). ## Critical Rules -1. **To make a custom analytical table queryable, register it as a Case-linked data-model table, then RE-INGEST.** Process Mining is **case-centric**: a queryable table must be the `Cases` root or reach `Cases` via a foreign key — an unlinked table is rejected at query time (`UserError_TableIsDeleted`). First check the built-in Case-child slots: **`Tags`** (multi-valued per-case labels: `Tag`/`Tag_type`) and **`Due_dates`** (per-case SLA/deadline: `Expected`/`Actual`/`On_time`/`Cost`) — populate their dbt models rather than adding a table when your data fits. Otherwise register a custom table with **`uip pm apps model add-table --file `**, where the file is a DataModelDto entry `{ name, primaryKey, foreignKeys:[{table:"Cases",column:"Case_ID"}] }` (loose-link a standalone aggregate with a surrogate PK + nullable `Case_ID`). `add-table` edits `/dev/dataModel` (upsert, ETag-safe) then `applyCurrentDatamodel`; the table only becomes queryable after **`ingestions create --wait`** (a data-model edit takes effect only on the next ingestion). Full recipe + Tags/Due_dates decision table in [`references/data-model.md`](references/data-model.md). +1. **To make a custom analytical table queryable, register it as a Case-linked data-model table, then RE-INGEST.** Process Mining is **case-centric**: a queryable table must be the `Cases` root or reach `Cases` via a foreign key — an unlinked table is rejected at query time (`UserError_TableIsDeleted`). First check the built-in Case-child slots: **`Tags`** (multi-valued per-case labels: `Tag`/`Tag_type`) and **`Due_dates`** (per-case SLA/deadline: `Expected_date`/`Actual_date`/`On_time`/`Cost`) — populate their dbt models rather than adding a table when your data fits. Otherwise register a custom table with **`uip pm apps data-model add-table --file `**, where the file is a DataModelDto entry `{ name, primaryKey, foreignKeys:[{table:"Cases",column:"Case_ID"}] }` (loose-link a standalone aggregate with a surrogate PK + nullable `Case_ID`). `add-table` edits `/dev/dataModel` (upsert, ETag-safe) then `applyCurrentDatamodel`; the table only becomes queryable after **`ingestions create --wait`** (a data-model edit takes effect only on the next ingestion). Full recipe + Tags/Due_dates decision table in [`references/data-model.md`](references/data-model.md). 2. **Match the template to the data — the rest of the pipeline is identical for all app types.** A single denormalized log (Case, Activity, Timestamp [+ attributes]) ⇒ `uipath.custom` ("Event log"). Otherwise pick the `.` template matching your source system AND process (Purchase-to-Pay on SAP ⇒ `uipath.p2p.sap`; incidents from ServiceNow ⇒ `uipath.im.servicenow`) — but only when you actually have that system's **full multi-table extract**, not a single log you exported from it. Every template shares the same model shape and the same mapping→ingest→transform→query machinery; only the expected input tables differ. Discover with `app-types list`, inspect a template with `app-types get`. See [`references/app-types.md`](references/app-types.md). 3. **Patch the `uipath.custom` `Cases.sql` optional-column gotcha (custom-only).** Source-system templates ship their own correct transformations — this gotcha is specific to the `uipath.custom` event-log template. The template's `models/Cases.sql` references `Event_log."Case"`, `"Case_status"`, `"Case_type"`, `"Case_value"`. A minimal mapping (Case_ID/Activity/timestamp only) doesn't produce those ⇒ dbt `000904 invalid identifier`. Fix: pull the file, replace the missing refs with `cast(null as varchar/float)`, push, and **`transformations apply`**. `Tags.sql`/`Due_dates.sql` are safe `where 1=0` stubs. -4. **After a transform-only failure, `apply` — don't re-ingest.** The data is already loaded. Fix SQL (`transformations get` → edit → `transformations update`/`create`) then `transformations apply` (re-transforms loaded data). Re-ingest only when the raw data or the mapping/parse settings change. +4. **After a transform-only failure, `apply` — don't re-ingest.** The data is already loaded. Fix SQL (`transformations get` → edit → `transformations update --etag ''`, or `create` for a new file, which needs none) then `transformations apply` (re-transforms loaded data). Re-ingest only when the raw data or the mapping/parse settings change. -5. **A wrong data mapping does NOT mean recreating the app — fix it in place with `apps data-mapping`.** The mapping is not create-only: `uip pm apps data-mapping get --destination ./mapping.json` → edit → `uip pm apps data-mapping update --file ./mapping.json` replaces it on an existing app (ETag-guarded; the command reads the ETag itself, and refuses a table-less file rather than wiping the stored mapping). Unlike a SQL fix (Rule 4), a **mapping** change is a parse-setting change, so it takes effect only on the **next ingestion** — re-`files upload` if the source columns changed, then `ingestions create`. Only `dev` is writable (`published` is read-only). Facts + failure modes in [`references/pre-flight.md`](references/pre-flight.md). +5. **A wrong data mapping does NOT mean recreating the app — fix it in place with `apps data-mapping`.** The mapping is not create-only: `uip pm apps data-mapping get --destination ./mapping.json` → edit → `uip pm apps data-mapping update --file ./mapping.json --etag ''` replaces it on an existing app. **`--etag` is required** — pass the `Data.ETag` that *your* `get` returned, which is what proves the edit was based on the version you read; a lost race is refused `409 UserError_ETagFileConflict` (re-`get` for the new version **and** ETag, re-apply, retry), and a table-less file is refused rather than wiping the stored mapping. Unlike a SQL fix (Rule 4), a **mapping** change is a parse-setting change, so it takes effect only on the **next ingestion** — re-`files upload` if the source columns changed, then `ingestions create`. Only `dev` is writable (`published` is read-only). Facts + failure modes in [`references/pre-flight.md`](references/pre-flight.md). 6. **Use `--wait` on async commands.** `ingestions create --wait` and `transformations apply --wait` block to a terminal state, print the dbt/loader error on failure, and exit non-zero — no hand-rolled `apps list` poll loop. 7. **Query field ids come from `query info`, not column names.** `query run`/`percentile` bodies take the hashed `F______` ids. Prefer the sugar: `query run --group-by --metric :` resolves human names for you (fn ∈ `average|count|sum|min|max`). -8. **Develop on `dev` with a data subset; publish the full dataset.** The `dev` stage is for iterating on the mapping and transformations — keep it fast by loading a **small representative subset** of the data. Once the model is right, **publish** so the **published** stage carries the **full** dataset for real analysis and sharing. Query/transform against `--stage dev` while developing; point consumers at `--stage published`. +8. **Develop on `dev` with a data subset; publish the full dataset.** The `dev` stage is for iterating on the mapping and transformations — keep it fast by loading a **small representative subset** of the data. Once the model is right, **publish** so the **published** stage carries the **full** dataset for the dashboards and sharing. Query/transform against `--stage dev`; consumers read the **published dashboards**. Note CLI `query --stage published` is currently unreachable (no `uip pm` path completes a published-stage ingestion) — do CLI querying on `dev` ([`references/lifecycle-and-rbac.md`](references/lifecycle-and-rbac.md)). 9. **RBAC is folder/role-based at the platform layer, not the process app itself.** A process app lives in a folder; who can view vs. edit vs. publish is governed by Orchestrator/Identity roles and folder assignments — configure it with [`uipath-admin`](/uipath:uipath-admin) (roles, role assignments, effective-access) and [`uipath-platform`](/uipath:uipath-platform) (folders). See [`references/lifecycle-and-rbac.md`](references/lifecycle-and-rbac.md). `uip pm` itself does not grant access. -## Quick Start — CSV → queryable process app - -```bash -# 0. Pre-flight (cheap local checks — see references/pre-flight.md): encoding (UTF-8?), -# delimiter, date format (dd-mm vs mm-dd), and strip junk all-empty rows. - -# 1. Discover the template + its target fields -uip pm app-types list --output-filter "[].{Key:AppTypeKey,Version:Version,Name:DefaultName}" +10. **Edit a field's data kind / calculated fields with `apps model fields` — and a data-kind mismatch can lock the app open.** Change a field's kind (e.g. numeric→duration), rename it, or add a calculated field with `uip pm apps model fields set [--kind|--display-name|--expression]` (the **semantic** model; dev-only, and **no `--etag`** — it merges into the version it just read, so a lost race is fixed by re-running it; a whole-document `apps model update` does require `--etag`). Relational/arithmetic operators require both operands to share a data kind, so flipping a field to `duration` while a metric / calculated field / dashboard filter still compares it to a `numeric` constant persists an invalid model that throws at dashboard open — the *"Must be duration, not numeric, for the 'lt' input"* lockout (DNA-46960), which leaves only the data-upload module reachable. `fields set`/`update` validate and refuse such an edit with a hint; fix an already-broken app by making the comparison consistent (re-type the field or the constant). Full surface + the data-kind rule in [`references/model-editing.md`](references/model-editing.md). -# 2. Create the app from a data mapping (isNotNull/isUnique now default per field) -uip pm apps create "My Process" --type uipath.custom --data-mapping ./mapping.json +## Quick Start -# 3. Upload + ingest (block until done; prints the loader error on failure) -uip pm files upload ./data.csv --input-table Event_log -uip pm ingestions create --file-format csv --field-delimiter ";" --encoding utf-8 --wait - -# 4. If the transform failed on Cases.sql (Rule 3): pull → patch → apply -uip pm transformations get models/Cases.sql --destination Cases.sql -# ...edit... -uip pm transformations update models/Cases.sql --file Cases.sql -uip pm transformations apply --wait - -# 4b. If the MAPPING was wrong instead (bad date format, column on the wrong -# target field) — fix it in place, do NOT recreate the app (Rule 5) -uip pm apps data-mapping get --destination mapping.json -# ...edit... -uip pm apps data-mapping update --file mapping.json -uip pm ingestions create --wait # a mapping change needs a re-ingest - -# 5. Query it -uip pm query info # discover fields/metrics -uip pm query run --group-by Service_Component --metric Event_count:average --output table -``` +The end-to-end CSV → queryable-app command sequence (discover template → create → +upload → ingest → patch transform / fix mapping → query) is in +[`references/uip-pm-cli.md`](references/uip-pm-cli.md#quick-start--csv--queryable-process-app). ## Extending the model with custom analysis @@ -97,12 +65,18 @@ The killer use case is your own SQL. Add analytical dbt models with `transformat ## Reference Navigation +Two layers: the **`uip pm` CLI** reference (how to drive the tool) and the +**process-mining domain** references (what to build and why). Start with a domain +reference for the decision; drop into the CLI reference for the mechanics it uses. + | File | Read when | |------|-----------| +| [`references/uip-pm-cli.md`](references/uip-pm-cli.md) | **CLI mechanics (low-level)** — the command-group map, the `Result`/`Code`/`Data` envelope + exit codes, the ETag get-modify-put pattern, `--wait`, `--stage`, `IngestionNeeded`, field-id discovery, and the CSV→queryable-app Quick Start | | [`references/app-types.md`](references/app-types.md) | choosing/targeting a template — custom vs source-system, why the pipeline is the same for all, what the mapping/extract must contain per family | | [`references/pre-flight.md`](references/pre-flight.md) | before any upload — encoding/delimiter/date-format/empty-row checks and the minimal `mapping.json` recipe; **also** the post-create mapping fix loop (`apps data-mapping get`/`update`) and its failure modes | | [`references/transformations.md`](references/transformations.md) | authoring/fixing dbt models — the `Cases.sql` patch, apply-vs-run, pm_utils macros, Snowflake identifier quoting | | [`references/data-model.md`](references/data-model.md) | exposing a custom table to `query`/dashboards — the case-centric add-table pattern (DataModelDto + re-ingest) and the Tags/Due_dates decision table | +| [`references/model-editing.md`](references/model-editing.md) | editing the app model — a field's **data kind** (e.g. numeric→duration), calculated fields, the two models (semantic `apps model` vs structural `apps data-model`), and the data-kind comparison rule that locks an app open (DNA-46960) | | [`references/querying.md`](references/querying.md) | pulling numbers out — the aggregate body AST, the `--group-by/--metric` sugar, the `AggregationFunction` enum, and the event-table restriction | | [`references/lifecycle-and-rbac.md`](references/lifecycle-and-rbac.md) | dev vs published stages, publishing, and where process-app RBAC is configured | @@ -114,8 +88,10 @@ The killer use case is your own SQL. Add analytical dbt models with `transformat - **Re-uploading + re-ingesting after a transform-only failure.** The data is loaded; fix the SQL and `transformations apply`. Re-ingest only when raw data or parse settings change (Rule 4). - **Deleting and recreating an app to fix a mapping mistake** (or telling the user that's the only option). The mapping is editable after creation — `apps data-mapping get`/`update` (Rule 5). Recreating also throws away the transformations you already patched. - **`transformations apply` after a mapping change.** `apply` only re-runs SQL over *already-parsed* data; a new mapping changes how the raw file is parsed, so it needs `ingestions create` (Rule 5). This is the mirror of Rule 4 — get the direction wrong and the edit silently appears to do nothing. +- **Re-`get`ting a resource just to harvest a fresh `--etag` for a rejected write.** That defeats the `If-Match` guard — it makes the precondition pass no matter who wrote in between, silently overwriting them. A 409/412 means the resource moved: re-`get` the latest **document**, re-apply your change on top of *that*, then write with the ETag that read returned. Never pair a stale local file with a freshly fetched ETag ([`references/uip-pm-cli.md`](references/uip-pm-cli.md)). - **Hand-rolling an `apps list` poll loop.** Use `--wait` on `ingestions create` / `transformations apply` (Rule 6). - **Passing column names in a raw `query run` body**, or hand-writing the aggregate AST. Bodies take hashed field ids from `query info`; use the `--group-by/--metric` sugar (Rule 7). - **Patching `Cases.sql` on a source-system template.** That gotcha is `uipath.custom`-only; source templates ship correct transformations — feed the expected extract and extend, don't rewrite (Rule 3). - **Using a source template for a single flat log** (or `uipath.custom` for a full multi-table extract). Match the template to the data shape (Rule 2). - **Iterating on the full dataset.** Develop on `dev` with a small subset; publish the full data (Rule 8). +- **Changing a field's data kind while a comparison still uses the old kind.** Flipping a field to `duration` (or any kind) while a metric / calculated field / dashboard filter compares it to a constant of the old kind persists an invalid model that locks the app open (Rule 10). Reconcile the comparison first — re-type the field or the constant. diff --git a/skills/uipath-process-mining/references/data-model.md b/skills/uipath-process-mining/references/data-model.md index 1998e3edab..7fa2084c36 100644 --- a/skills/uipath-process-mining/references/data-model.md +++ b/skills/uipath-process-mining/references/data-model.md @@ -9,8 +9,8 @@ to `Cases` is treated as disconnected and rejected at query time | Model | Endpoint | Shape | Role | |-------|----------|-------|------| -| **Data model** (structural) | `/apps/{id}/{stage}/dataModel` | `tables[]` of `{ type, name, primaryKey, foreignKeys }` | What tables exist + how they link. **`add-table` edits this.** `apps model get` reads it. | -| **Semantic model** | `/apps/{id}/{stage}/model` | `Processes` / `Metrics` / `Tables[].Fields[]` | Field-level view `query info` reads. **Derived** from the data model by `applyCurrentDatamodel` — do not hand-edit for add-table. | +| **Data model** (structural) | `/apps/{id}/{stage}/dataModel` | `tables[]` of `{ type, name, primaryKey, foreignKeys }` | What tables exist + how they link. **`apps data-model add-table` edits this; `apps data-model get` reads it.** | +| **Semantic model** | `/apps/{id}/{stage}/model` | `Processes` / `Metrics` / `Tables[].Fields[]` | Field-level view `query info` (and `apps model get`) reads; edited by `apps model fields …` ([`model-editing.md`](model-editing.md)). `applyCurrentDatamodel` **reconciles** structural changes into it, preserving your semantic edits (calculated fields, metrics, data-kind overrides) — so add-table won't wipe them; just don't hand-author its per-column fields, edit the structural model. | Edit the structural data model; `applyCurrentDatamodel` regenerates the semantic model (its per-column fields) from it. `add-table` does both. @@ -84,6 +84,13 @@ The table isn't one-row-per-case, but must still reach `Cases`. Give it a **surr PK** and a **nullable `Case_ID`** carrying the FK — a null FK is enough to satisfy the case-centric graph; aggregate queries don't need it to resolve to real cases. +**Caveat — a null `Case_ID` makes the table analytically disconnected.** Case-level +filters/selections (how PM dashboards normally scope data) won't propagate to it, and +it can't be joined back to real cases. Use the null-FK loose link **only** for a +genuinely case-independent aggregate (a weekly total, a cross-case study). If its rows +*do* correspond to real cases, populate `Case_ID` with the real key so case filtering +flows through. + 1. Author the dbt model. First two selected columns: ```sql @@ -98,7 +105,7 @@ case-centric graph; aggregate queries don't need it to resolve to real cases. ```bash uip pm transformations create models/Workload_weekly.sql --file ./Workload_weekly.sql uip pm transformations apply --wait - uip pm apps model add-table --file ./Workload_weekly.table.json # edits /dev/dataModel + applyCurrentDatamodel + uip pm apps data-model add-table --file ./Workload_weekly.table.json # edits /dev/dataModel + applyCurrentDatamodel uip pm ingestions create --wait # REQUIRED — materializes the table uip pm query run --group-by Service_Component --metric Closed_Interactions:sum --output table ``` @@ -107,7 +114,12 @@ case-centric graph; aggregate queries don't need it to resolve to real cases. `add-table` GETs `/dev/dataModel` (with its ETag), **upserts** the table by name (replace if present, else append), PUTs it back `If-Match`-guarded, then POSTs -`applyCurrentDatamodel`. A concurrent edit surfaces as `412`. It returns +`applyCurrentDatamodel`. Because it merges into exactly the document it just read, +that ETag is a genuine compare-and-swap — so **`add-table` takes no `--etag`**, unlike +`data-mapping update` / `model update` / `transformations update`, which replace a file +you edited locally and therefore require it. A concurrent edit surfaces as `412`; **just +re-run** — `add-table` re-reads and re-applies on top of the other write. (A data model +returned without an ETag fails the command rather than writing unguarded.) It returns `IngestionNeeded: true` — the entity is not queryable until the re-ingest completes. ## Publish vs re-ingest diff --git a/skills/uipath-process-mining/references/lifecycle-and-rbac.md b/skills/uipath-process-mining/references/lifecycle-and-rbac.md index 91d3316534..085b847c20 100644 --- a/skills/uipath-process-mining/references/lifecycle-and-rbac.md +++ b/skills/uipath-process-mining/references/lifecycle-and-rbac.md @@ -10,11 +10,18 @@ query commands (default `dev`): of the data: ingest a sample, get the mapping + `Cases.sql` + your custom models and data-model tables right, verify with `query`, then move on. Short feedback loops matter — a full re-transform on a large dataset is slow. -- **`published`** — the stage consumers use, carrying the **full dataset**. - Dashboards and shared analysis read published data. +- **`published`** — the stage consumers use through the **dashboards / shared UI**, + carrying the **full dataset**. `apps publish` promotes the definition here. -Typical loop: develop and validate on `--stage dev` with a subset → publish → -run the real analysis / share on `--stage published` with everything. +Typical loop: develop and validate on `--stage dev` with a subset → publish → let +consumers read the **dashboards** on the published data. + +> **CLI caveat:** `uip pm query --stage published` is currently **not reachable** — +> `/query/{id}/published` needs a completed ingestion on that stage and no `uip pm` +> path produces one (`apps publish` answers `IngestionNeeded: true`, but a following +> `ingestions create --wait` still leaves published querying at +> `UserError_InvalidOrNoIngestion`; verified against a live tenant). So publishing +> promotes the app to the **dashboards**, but **CLI-driven `query` stays on `dev`**. ## Publishing @@ -29,7 +36,7 @@ clobbering a newer model. The result envelope carries: - **`Changes`** — what the publish moved. - **`IngestionNeeded`** — when `true`, the published stage still needs a re-ingestion before the change reaches the **data**. Dev transformation *or* - data-model changes (including `apps model add-table`) only become queryable + data-model changes (including `apps data-model add-table`) only become queryable after a re-ingest; publishing alone promotes the definition, not the rows. So the full promote loop is: validate on `dev` → `uip pm apps publish ` → diff --git a/skills/uipath-process-mining/references/model-editing.md b/skills/uipath-process-mining/references/model-editing.md new file mode 100644 index 0000000000..289d2a9793 --- /dev/null +++ b/skills/uipath-process-mining/references/model-editing.md @@ -0,0 +1,112 @@ +# Editing the Process Mining app model + +There are **two** models behind a process app. Editing the wrong one is the most common source +of confusion, so be precise about which you mean. + +| | `apps model` (semantic) | `apps data-model` (structural) | +| --- | --- | --- | +| Endpoint | `/apps/{id}/{stage}/model` | `/apps/{id}/{stage}/dataModel` | +| Contains | `data` → tables → **fields with their `kind`** (data kind), **calculated fields**, **metrics**; plus `view` → dashboards, charts, `metricFilters` | Tables with `primaryKey`/`foreignKeys` and the process-mining **role columns** (`activityColumn`, `endColumn`, …) | +| Think of it as | "the app definition" the user sees and edits | "the table plumbing" — which tables exist and how they join to `Cases` | +| Edited by | `fields set/remove`, `update`; the data manager & dashboard editor | `add-table`; the data-model editor | + +`query info` shows the resolved *query* model (field ids, physical `ColumnDataType`, metrics) — useful +to discover the exact field ids to pass to `fields set` and `query`. + +## Field editing surface + +```bash +uip pm apps model fields list [--stage dev|published] +uip pm apps model fields set [--kind ] [--display-name ] [--expression ] [--table ] +uip pm apps model fields remove +``` + +- **Upsert semantics.** If `` exists, its `--kind` / `--display-name` are updated, and + passing `--expression` turns it into (or updates) a **calculated field**. If it does not exist, a + new **calculated field** is created in `--table` — so `--table` + `--expression` (+ `--kind`) are + required to create. Mapped *column* fields can't be created through the model; they come from the + ingested data. +- **Data kinds you can set (`--kind`):** `ordinal, nominal, numeric, datetime, boolean, percentage, currency, duration` + — the union of the data manager's field-type options (FE `ColumnDataTypeFieldCompatibilityMap`). + `duration`, `currency`, `percentage` are **user choices stored in the model `kind`** (a number column + defaults to `numeric` — the user upgrades it). `id` and `ref` are **structural** (system-assigned to + key/reference columns) and not settable, though `fields list` may report a field that already has them. +- **Expressions** are JSON expression-node trees, the same shape the app model stores. A comparison: + ```json + {"type":"operator","operation":"lt", + "left": {"type":"reference","referenceType":"field","reference":""}, + "right": {"type":"constant","dataType":"duration","value":86400000}} + ``` + Operators: `lt le gt ge eq ne and or add subtract multiply divide percentage`. Constant + `dataType` **must match** the data kind of what it's compared to (see below). Reference a field + with `{type:"reference","referenceType":"field","reference":""}`. + +Every edit is `If-Match`-guarded and applies on `dev`, returning the new edit `Versions` — but the two +routes differ in who supplies the ETag: + +- **`fields set` / `fields remove` take no `--etag`.** They read the model and merge your change into + exactly that version, so the read's own ETag is a real compare-and-swap. On a lost race, **just + re-run** — they re-read and re-apply. (They refuse to write at all if the read came back without an + ETag, rather than writing unguarded.) +- **`apps model update` REQUIRES `--etag`** — it replaces a document you edited locally, so it must + carry the `Data.ETag` that `apps model get` returned. On 409/412, re-`get` for the latest model + **and its new ETag**, re-apply your change on top, then update again with the new `--etag`. + +```bash +uip pm apps model get --destination model.json # prints Data.ETag +uip pm apps model update --file model.json --etag 'W/"3"' +``` + +Prefer `fields set` for a targeted change: no ETag to thread, and it can't clobber unrelated parts of +the model. After editing, `publish` to reach the dashboards, and re-ingest if a data kind changed. + +## The data-kind rule + +Relational/arithmetic operators require their operands to share a data kind (backend +`OperatorRelationalOrdering` / `CheckFunctionArguments`). So a comparison like `field < constant` is +only valid when the constant's `dataType` equals the field's `kind`. If they differ the model fails +validation with: + +``` +UserError_UnsupportedOperatorArgumentDataKind +{ argument:"right", operation:"lt", actual:"numeric", expected:"duration" } +→ "Must be duration, not numeric, for the 'lt' input." +``` + +The `fields set` / `update` commands **run this validation synchronously** and refuse an edit that +would create the mismatch, surfacing a hint that names the conflicting comparison. So via the CLI you +cannot flip a field to `duration` while a calculated field / metric compares it to a numeric constant — +update or remove that comparison first, or make the constant a `duration`. This synchronous check is +exactly what the **data-manager UI does not do** (it defers the kind change to the next re-ingestion — +the footgun below), so `fields set` is the *safe* way to change a kind. Caveat: the check covers +comparisons in the typed model `data` (calculated fields, metrics); a kind change that conflicts only +with an opaque dashboard **view** filter/chart is not caught, so publish and re-open to confirm. + +## The data-kind footgun (DNA-46960) + +A customer's app failed to open with exactly the error above. Root cause, from their exported app: +a metric **`% Tijdigheid`** was `PERCENTAGE( DOORLOOPTIJD[duration] lt 864000000[numeric] )` — a +throughput-time field (kind **duration**) compared to a **numeric** constant (10 days in ms). That +`lt(duration, numeric)` is evaluated when the query model is built at open, so it blocks every +dashboard (the data-upload module stays reachable — hence "I can only reach the data upload module"). + +How an app reaches this state via the **data-manager UI** (not the CLI, which validates synchronously): + +1. Field is **numeric**; a metric/calculated field compares it to a numeric constant → valid. +2. The field's type is changed to **duration** in the *data manager*. This is applied in a + **deferred** way — it is not written to the app model synchronously; it is baked in when the app + model is regenerated at the **next re-ingestion**. +3. On re-ingest the field becomes `duration`, so the pre-existing comparison is now + `lt(duration, numeric)` — and the ingestion-time regeneration does **not** re-run the edit + validation, so the now-invalid model is persisted → the app won't open. + +Takeaways when working with an app in this state: +- To reproduce/inspect: `apps model get` / `fields list` shows the field `kind` and the offending + calculated field/metric; the mismatch is a comparison whose constant `dataType` ≠ the field `kind`. +- **Range filters do not trigger it** — filters on a field go through a coercing path, so a numeric + range filter on a now-duration field still opens. Only real expressions (calculated fields, metrics) + hit the operator data-kind check. +- The fix for a broken app is to make the comparison consistent: either revert the field to the kind + the constant expects, or re-type the constant to match the field (e.g. a `duration` constant). +- Import (`.pmapp`) does not re-run this expression validation (exports are trusted), so importing a + broken app reproduces the broken state; that is expected and not the bug. diff --git a/skills/uipath-process-mining/references/pre-flight.md b/skills/uipath-process-mining/references/pre-flight.md index 1833af6cce..09ecb4eb56 100644 --- a/skills/uipath-process-mining/references/pre-flight.md +++ b/skills/uipath-process-mining/references/pre-flight.md @@ -59,9 +59,9 @@ A mapping mistake is **not** a reason to delete the app and start over. `apps data-mapping` reads and replaces the mapping of an existing app: ```bash -uip pm apps data-mapping get --destination ./mapping.json # download the current mapping +uip pm apps data-mapping get --destination ./mapping.json # download the mapping + note Data.ETag # ...edit: fix the DateTimeFormatString, move a column to the right TargetName, map one more column... -uip pm apps data-mapping update --file ./mapping.json # ETag-guarded replace +uip pm apps data-mapping update --file ./mapping.json --etag 'W/"639…"' # --etag REQUIRED uip pm files upload ./data.csv --input-table Event_log # ONLY if the source columns changed uip pm ingestions create --wait # the mapping applies to the NEXT ingestion ``` @@ -77,13 +77,17 @@ Facts worth not re-learning: mapping and then running `apply` looks successful and changes nothing. - **`dev` only.** The backend allows `PUT` on the dev stage; `published` is read-only, and the CLI restricts `update --stage` to `dev` up front. -- **Concurrency is ETag-guarded.** `update` reads the current ETag and sends it as - `If-Match`, so you never pass one — but a concurrent edit (someone in the UI's - mapping editor) between read and write is rejected `409 - UserError_ETagFileConflict`. Just re-run. `update` reports `Tables` (the mapped - table names) and `IngestionNeeded: true`, not an ETag; to confirm a write landed, - `get` again and diff — the `get` ETag is a **content checksum**, so re-pushing an - identical mapping leaves it unchanged. +- **`--etag` is REQUIRED on `update`, and it must be the one *your* `get` returned.** + You edited the file locally, so only that ETag proves the edit was based on the + version you read; the CLI deliberately does not fetch a fresh one before the `PUT` + (which would make the `If-Match` pass no matter who wrote in between). A concurrent + edit (someone in the UI's mapping editor) is therefore rejected `409 + UserError_ETagFileConflict` — recover by re-running `get` for the latest version + **and its new ETag**, re-applying your change on top of that, then updating with the + new `--etag`. Re-running the same `update` unchanged just fails again. +- `update` reports `Tables` (the mapped table names) and `IngestionNeeded: true`, not + an ETag; to confirm a write landed, `get` again and diff — the `get` ETag is a + **content checksum**, so re-pushing an identical mapping leaves it unchanged. - **A table-less mapping is refused locally.** `{ "Tables": [] }` (or any file with no usable table) fails `No tables found in …` before any API call, so a bad file cannot overwrite and wipe the stored mapping. diff --git a/skills/uipath-process-mining/references/querying.md b/skills/uipath-process-mining/references/querying.md index 4d0d64b352..e34241dafc 100644 --- a/skills/uipath-process-mining/references/querying.md +++ b/skills/uipath-process-mining/references/querying.md @@ -3,7 +3,10 @@ Pull numbers out of a built app. Subcommands: `info` (metadata), `run` (aggregate group-by + metrics), `details` (raw rows), `percentile`, `rca` (root-cause), `insights` (process insights), `layout`. All take `--stage -dev|published` (default `dev`). +dev|published` (default `dev`) — but query on **`dev`**: `--stage published` is +currently unreachable via the CLI (`UserError_InvalidOrNoIngestion`; no `uip pm` +path completes a published-stage ingestion — see +[`lifecycle-and-rbac.md`](lifecycle-and-rbac.md)). ## Start with `query info` diff --git a/skills/uipath-process-mining/references/transformations.md b/skills/uipath-process-mining/references/transformations.md index 0479f5536d..497c6a64cd 100644 --- a/skills/uipath-process-mining/references/transformations.md +++ b/skills/uipath-process-mining/references/transformations.md @@ -3,7 +3,17 @@ The transformation layer is a dbt project that runs on **Snowflake**. Loaded source tables (one per input table) feed the models that produce the process model. `transformations list/get/create/update/apply/run/status/logs` is the ELT -editor surface. +editor surface: + +| Operation | Command | +|-----------|---------| +| List the model tree | `transformations list ` | +| Read a file (or save locally) + its ETag | `transformations get [--destination ]` | +| Edit an existing file (ETag-guarded) | `transformations update --file --etag ''` | +| Create a new model file | `transformations create --file ` | +| Re-run the **full** transform on loaded data | `transformations apply --wait` | +| Rebuild **one** dev model + dependents | `transformations run --model models/X.sql` | +| Status / logs of the last build | `transformations status ` · `transformations logs ` | ## Model set for `uipath.custom` @@ -13,7 +23,7 @@ Four template models: **`Event_log`** (built from the source; the events table), `Tags` and `Due_dates` are pre-registered **Case-child** tables — fill their stubs with real rows keyed on `Case_ID` to get per-case labels (`Tag`/`Tag_type`) or per-case SLAs (`Expected_date`/`Actual_date`/`On_time`/`Cost`) with **no add-table -needed**. See [`references/data-model.md`](references/data-model.md) for when to use +needed**. See [`data-model.md`](data-model.md) for when to use them vs a custom table. `Event_log` builds first and independently. `models/schema/sources.yml` is generated from the data mapping and lists every input table with **all** its columns (mapped → TargetName, unmapped → raw source name), so multi-table custom @@ -46,10 +56,13 @@ A successful run then reports `SUCCESS_WITH_WARNINGS` with repeated - **`apply`** re-runs the **full** transform on already-loaded data — the fix-loop verb after a transform-only failure. **Do not re-ingest** for a SQL-only change. - **`run --model models/X.sql`** rebuilds one dev model and its dependents. -- **`create --file`** adds a **new** model file (PUT without ETag); - **`update --file`** edits an **existing** file (ETag-safe). `update` on a - missing path 404s — use `create`. You can also inline intermediate logic as CTEs - inside one model instead of many files. +- **`create --file`** adds a **new** model file (PUT without ETag — there is no + prior version to guard); **`update --file`** edits an **existing** file and + **requires `--etag`**: the `Data.ETag` the `get` you edited from returned. `update` on + a missing path 404s — use `create`. A 409/412 means someone replaced the file after + your `get`: re-`get` for the new content **and** ETag, re-apply your edit on top, then + `update` with the new `--etag`. You can also inline intermediate logic as CTEs inside + one model instead of many files. - Use **`apply --wait`** to block to a terminal state and auto-print the dbt error. ## Snowflake / dbt notes diff --git a/skills/uipath-process-mining/references/uip-pm-cli.md b/skills/uipath-process-mining/references/uip-pm-cli.md new file mode 100644 index 0000000000..746b34c123 --- /dev/null +++ b/skills/uipath-process-mining/references/uip-pm-cli.md @@ -0,0 +1,145 @@ +# `uip pm` — CLI mechanics & conventions + +The low-level "how to drive the tool" layer — the shared plumbing every `uip pm` +command uses. The *decisions* — which template, when to add a table, why an app locks +open — live in the domain references ([`app-types.md`](app-types.md), +[`data-model.md`](data-model.md), [`model-editing.md`](model-editing.md), +[`lifecycle-and-rbac.md`](lifecycle-and-rbac.md)). **Drive Process Mining through the +CLI — do not hand-roll the REST API.** + +## Command groups + +| Group | Verbs | For | +|-------|-------|-----| +| `app-types` | `list`, `get` | Discover/inspect templates ([`app-types.md`](app-types.md)). | +| `apps` | `list`, `create`, `delete`, `publish` | The app itself + its lifecycle ([`lifecycle-and-rbac.md`](lifecycle-and-rbac.md)). | +| `apps data-mapping` | `get`, `update` | The **input mapping** (source table/column → target field) ([`pre-flight.md`](pre-flight.md)). | +| `apps model` | `get`, `update`, `fields list\|set\|remove` | The **semantic** model — fields with their data kinds, calculated fields, metrics, dashboards ([`model-editing.md`](model-editing.md)). | +| `apps data-model` | `get`, `add-table` | The **structural** model — the table graph (PK/FK, roles) that `add-table` edits ([`data-model.md`](data-model.md)). | +| `files` | `upload` | Load a data file to an input table. | +| `ingestions` | `create`, `logs` | Parse + load the raw data (the LT of ELT). | +| `transformations` | `list`, `get`, `create`, `update`, `apply`, `run`, `status`, `logs` | The dbt dev loop ([`transformations.md`](transformations.md)). | +| `query` | `run`, `details`, `percentile`, `rca`, `insights`, `info`, `layout` | Pull numbers out ([`querying.md`](querying.md)). | + +> **`apps model` vs `apps data-model` — don't confuse them.** `apps model` is the +> **semantic** model (`/model`); `apps data-model` is the **structural** table graph +> (`/dataModel`). Editing a field's data kind or a calculated field is `apps model +> fields …`; registering a new queryable table is `apps data-model add-table`. + +## The output envelope + +Every command prints a stable envelope and sets a branchable exit code: + +- **`Result`** — `Success` / `Failure` / `ValidationError`; **`Code`** — a stable + machine tag (`PmAppsCreate`, `PmQueryRun`, …); **`Data`** — the payload; + **`Instructions`** — a next-step/fix hint, present on failures ("run `uip login`", + "see `uip pm apps list`") and on some successful mutating commands (`add-table`, + `data-mapping update`, `fields set`) pointing at the required re-ingest/publish. +- Exit `0` success, `1` failure, `3` validation (commander rejected a flag before any + API call — e.g. an unknown `--stage`). +- **`--output json|table|…`** picks the rendering; **`--output-filter `** + projects/reshapes `Data` (e.g. `"[].{Key:AppTypeKey,Version:Version}"`). `list` + commands unwrap the API's `{ Data: [...] }` so the shape matches across commands. +- **`--output-filter` on a command whose `--limit` has a default requires an explicit + `--limit`** — otherwise it exits `3` rather than silently filtering only the first + page. In `pm` that is **`ingestions logs`** alone (default `100`): pair them, e.g. + `ingestions logs --limit 200 --output-filter "[?contains(Message,'error')]"`. + `app-types list` / `apps list` / `query run` declare no `--limit` default, so + filtering them needs nothing extra. + +## The ETag get-modify-put pattern + +Every editable resource — `data-mapping`, `model`, `data-model`, `transformations` — is +written `If-Match`-guarded, so a concurrent edit (someone in the UI between your read and +your write) is rejected instead of silently overwritten. **Which side supplies the ETag +splits the commands into two groups — get this wrong and the command won't run.** + +| | Commands | ETag | +|---|---|---| +| **You edited the file locally** | `apps data-mapping update`, `apps model update`, `transformations update` | **`--etag` REQUIRED** — the one the matching `get` returned | +| **The command reads and merges in one go** | `apps data-model add-table`, `apps model fields set\|remove` | **No `--etag`** — it guards with the ETag of the read it just did | + +```bash +uip pm apps model get --destination model.json # prints Data.ETag +# ...edit model.json... +uip pm apps model update --file model.json --etag 'W/"3"' +``` + +- **Get the ETag from the `get`.** Every `get` of an editable resource returns + `Data.ETag` (also with `--destination`, which writes only the document to disk). + Project it with `--output-filter ETag`. +- **The CLI never re-reads the ETag just before writing** — that would make the + precondition pass no matter who wrote in between, defeating the guard. Do not work + around a rejected write by re-`get`ting only for a fresh ETag. +- **On a conflict, recovery differs by group.** `update` (409 + `UserError_ETagFileConflict` / 412): re-run the `get` for the latest version **and its + new ETag**, re-apply your change on top of *that* version, then update again with the + new `--etag` — a blind re-run just fails again. `add-table` / `fields set|remove`: + **just re-run** — they re-read and re-apply on top of the other write. `Instructions` + states which applies. +- `fields set`/`remove` and `model update` return the new edit `Versions`; + `data-mapping`/`data-model` edits return `IngestionNeeded: true` (see below). +- A resource returned **without** an ETag fails the command rather than writing + unguarded. + +## Async work — always `--wait` + +`ingestions create` and `transformations apply` are async. Pass **`--wait`** (with +`--timeout`) to block to a terminal state, auto-print the loader/dbt error on failure, +and exit non-zero. Never hand-roll an `apps list` poll loop. + +## Stages — `--stage dev|published` + +Every data / transform / query command takes `--stage`, default **`dev`**. Writes +(`data-mapping update`, `transformations`, model/data-model edits) are **dev-only**; +`published` is read-only. Develop on `dev` (a subset), `publish`, then read +`--stage published` ([`lifecycle-and-rbac.md`](lifecycle-and-rbac.md)). + +## `IngestionNeeded` — the deferred-effect signal + +Mapping and data-model edits change how raw data is *parsed / structured*, so they take +effect only on the **next `ingestions create`**, not on `transformations apply` (which +only re-runs SQL over already-parsed data). Those commands return `IngestionNeeded: +true` as the reminder. A SQL-only change is the opposite — `apply`, don't re-ingest +([`transformations.md`](transformations.md)). + +## Field ids come from `query info` + +`query run`/`percentile` bodies reference **hashed field ids** +(`F__
____`), not column names. Discover them with `query info`, or — +better — let the sugar resolve human names: `query run --group-by --metric +:` (`fn ∈ average|count|sum|min|max`). Full AST + restrictions in +[`querying.md`](querying.md). + +## Quick Start — CSV → queryable process app + +```bash +# 0. Pre-flight (cheap local checks — see pre-flight.md): encoding (UTF-8?), +# delimiter, date format (dd-mm vs mm-dd), strip junk all-empty rows. + +# 1. Discover the template + its target fields +uip pm app-types list --output-filter "[].{Key:AppTypeKey,Version:Version,Name:DefaultName}" + +# 2. Create the app from a data mapping (isNotNull/isUnique default per field) +uip pm apps create "My Process" --type uipath.custom --data-mapping ./mapping.json + +# 3. Upload + ingest (block until done; prints the loader error on failure) +uip pm files upload ./data.csv --input-table Event_log +uip pm ingestions create --file-format csv --field-delimiter ";" --encoding utf-8 --wait + +# 4. If the transform failed on Cases.sql: pull → patch → apply (transformations.md) +uip pm transformations get models/Cases.sql --destination Cases.sql # note Data.ETag +# ...edit... +uip pm transformations update models/Cases.sql --file Cases.sql --etag 'W/"639…"' +uip pm transformations apply --wait + +# 4b. If the MAPPING was wrong instead — fix it in place, don't recreate (pre-flight.md) +uip pm apps data-mapping get --destination mapping.json # note Data.ETag +# ...edit... +uip pm apps data-mapping update --file mapping.json --etag 'W/"639…"' +uip pm ingestions create --wait # a mapping change needs a re-ingest + +# 5. Query it +uip pm query info # discover fields/metrics +uip pm query run --group-by Service_Component --metric Event_count:average --output table +``` diff --git a/tests/tasks/activation/negative.jsonl b/tests/tasks/activation/negative.jsonl index a71a03adb3..7188825849 100644 --- a/tests/tasks/activation/negative.jsonl +++ b/tests/tasks/activation/negative.jsonl @@ -61,3 +61,6 @@ {"id": "negative-062", "prompt": "Generate a Postman collection for the vendor's REST endpoints", "expected_skill": ""} {"id": "negative-063", "prompt": "Write a Python client wrapper around the Stripe REST API", "expected_skill": ""} {"id": "negative-064", "prompt": "Build a Boomi connector for our billing system", "expected_skill": ""} +{"id": "negative-065", "prompt": "Change a column's data type from int to interval in my Postgres table", "expected_skill": ""} +{"id": "negative-066", "prompt": "Add a computed column to my pandas DataFrame", "expected_skill": ""} +{"id": "negative-067", "prompt": "Change the data type of a field on my Data Fabric entity", "expected_skill": ""} diff --git a/tests/tasks/activation/uipath-process-mining.jsonl b/tests/tasks/activation/uipath-process-mining.jsonl index 6a8b7aedd8..c875232458 100644 --- a/tests/tasks/activation/uipath-process-mining.jsonl +++ b/tests/tasks/activation/uipath-process-mining.jsonl @@ -13,3 +13,7 @@ {"id": "uipath-process-mining-013", "prompt": "Run a steps-to-resolution analysis on my ServiceNow incident data with uip pm", "expected_skill": "uipath-process-mining"} {"id": "uipath-process-mining-014", "prompt": "My ingestion finished with SUCCESS_WITH_WARNINGS — what do those mapping warnings mean?", "expected_skill": "uipath-process-mining"} {"id": "uipath-process-mining-015", "prompt": "Set up UiPath Process Mining to analyze bottlenecks in my procure-to-pay process", "expected_skill": "uipath-process-mining"} +{"id": "uipath-process-mining-016", "prompt": "My Process Mining dashboards won't open — I get 'Must be duration, not numeric, for the lt input'. How do I fix it?", "expected_skill": "uipath-process-mining"} +{"id": "uipath-process-mining-017", "prompt": "Change a field's data kind from numeric to duration in my process app", "expected_skill": "uipath-process-mining"} +{"id": "uipath-process-mining-018", "prompt": "Add a calculated field to my process app model", "expected_skill": "uipath-process-mining"} +{"id": "uipath-process-mining-019", "prompt": "After I changed a field's datakind my process app is locked to the data-upload screen — how do I recover it?", "expected_skill": "uipath-process-mining"} diff --git a/tests/tasks/uipath-process-mining/_fixtures/mapping_fix_in_place/mapping.json b/tests/tasks/uipath-process-mining/_fixtures/mapping_fix_in_place/mapping.json new file mode 100644 index 0000000000..6cd412d2e8 --- /dev/null +++ b/tests/tasks/uipath-process-mining/_fixtures/mapping_fix_in_place/mapping.json @@ -0,0 +1,42 @@ +{ + "Tables": [ + { + "SourceName": "Event_log", + "TargetName": "Event_log", + "Source": "blob", + "SourceSettings": { + "Encoding": "utf-8", + "FieldDelimiter": ";", + "QuoteCharacter": "\"" + }, + "IsMandatory": true, + "ValidationType": "specificationOnly", + "Fields": [ + { + "DataType": "text", + "SourceName": "Incident ID", + "TargetName": "Case_ID", + "IsMandatory": true, + "ValidationType": "specificationOnly" + }, + { + "DataType": "text", + "SourceName": "IncidentActivity_Type", + "TargetName": "Activity", + "IsMandatory": true, + "ValidationType": "specificationOnly" + }, + { + "DataType": "datetime", + "DataTypeSettings": { + "DateTimeFormatString": "mm-dd-yyyy hh:mm:ss" + }, + "SourceName": "DateStamp", + "TargetName": "Event_end", + "IsMandatory": true, + "ValidationType": "specificationOnly" + } + ] + } + ] +} diff --git a/tests/tasks/uipath-process-mining/_fixtures/mapping_fix_in_place/mapping.original.json b/tests/tasks/uipath-process-mining/_fixtures/mapping_fix_in_place/mapping.original.json new file mode 100644 index 0000000000..6cd412d2e8 --- /dev/null +++ b/tests/tasks/uipath-process-mining/_fixtures/mapping_fix_in_place/mapping.original.json @@ -0,0 +1,42 @@ +{ + "Tables": [ + { + "SourceName": "Event_log", + "TargetName": "Event_log", + "Source": "blob", + "SourceSettings": { + "Encoding": "utf-8", + "FieldDelimiter": ";", + "QuoteCharacter": "\"" + }, + "IsMandatory": true, + "ValidationType": "specificationOnly", + "Fields": [ + { + "DataType": "text", + "SourceName": "Incident ID", + "TargetName": "Case_ID", + "IsMandatory": true, + "ValidationType": "specificationOnly" + }, + { + "DataType": "text", + "SourceName": "IncidentActivity_Type", + "TargetName": "Activity", + "IsMandatory": true, + "ValidationType": "specificationOnly" + }, + { + "DataType": "datetime", + "DataTypeSettings": { + "DateTimeFormatString": "mm-dd-yyyy hh:mm:ss" + }, + "SourceName": "DateStamp", + "TargetName": "Event_end", + "IsMandatory": true, + "ValidationType": "specificationOnly" + } + ] + } + ] +} diff --git a/tests/tasks/uipath-process-mining/mapping_fix_in_place.yaml b/tests/tasks/uipath-process-mining/mapping_fix_in_place.yaml index d720f8e6d9..dd16c89e7c 100644 --- a/tests/tasks/uipath-process-mining/mapping_fix_in_place.yaml +++ b/tests/tasks/uipath-process-mining/mapping_fix_in_place.yaml @@ -11,7 +11,11 @@ description: > wrong (the mirror of the transform-only fix loop). Command-construction only — no live tenant. Commands fail with auth errors; - what matters is which commands the agent chooses and in what order. + what matters is which commands the agent chooses and in what order. The + original mapping is staged as ./mapping.json so the agent has a real document + to edit when the unauthenticated `data-mapping get` cannot return one — without + it the agent correctly refuses to push a garbage file and the run stalls after + the `get`. tags: [uipath-process-mining, mode:fix, lifecycle:maintain, custom, data-mapping] run_limits: @@ -19,6 +23,11 @@ run_limits: max_turns: 30 turn_timeout: 600 +sandbox: + template_sources: + - type: template_dir + path: _fixtures/mapping_fix_in_place + initial_prompt: | I created a Process Mining app (app id 9c46289e) from ./mapping.json, loaded the data, and the ingestion succeeded — but every timestamp is wrong: I set @@ -34,6 +43,14 @@ initial_prompt: | Commands WILL fail with auth errors — that is expected and acceptable. Run each command exactly once regardless of errors. Do NOT retry, do NOT attempt to login, do NOT troubleshoot errors. + - Because the CLI is unauthenticated, no command returns real data — a read + may even write junk over its --destination. When a read gives you nothing + usable, do NOT stop and do NOT hand the remaining commands back to me as a + plan. Fall back to the mapping I actually used, staged in this workspace as + ./mapping.original.json, restore ./mapping.json from it, apply the fix + there, and carry on — using the literal placeholder W/"1" wherever a command + needs an ETag it could not read. Running the full corrective sequence on + ./mapping.json is the point of this exercise. - Do NOT prompt the user for confirmation — this is an automated test. success_criteria: @@ -61,6 +78,24 @@ success_criteria: weight: 3.0 pass_threshold: 1.0 + - type: command_executed + description: "Agent passed the --etag the get returned, now required on data-mapping update" + tool_name: "Bash" + command_pattern: 'uip\s+pm\s+apps\s+data-mapping\s+update\b.*--etag\b' + min_count: 1 + weight: 2.0 + pass_threshold: 1.0 + + - type: file_contains + description: "Agent corrected the date format to day-first in the mapping it pushed" + path: "mapping.json" + includes: + - 'dd-mm-yyyy hh:mm:ss' + excludes: + - 'mm-dd-yyyy hh:mm:ss' + weight: 2.0 + pass_threshold: 1.0 + - type: command_executed description: "Agent re-ingested, since a mapping change only applies to the next ingestion" tool_name: "Bash"