From a5b37d04f383e9d5e6d8bacb61f17eca6928e809 Mon Sep 17 00:00:00 2001 From: Monir Imamverdi Date: Thu, 30 Jul 2026 09:14:33 -0700 Subject: [PATCH] feat(bpmn): derive package metadata with refresh --- .github/workflows/test-helpers.yml | 16 +++ skills/uipath-maestro-bpmn/SKILL.md | 27 ++++- ...ration-service-activity-authoring-guide.md | 4 +- .../references/operate/CAPABILITY.md | 6 +- .../references/operate/references/ship.md | 19 +++- .../local-metadata-regeneration-guide.md | 65 +++++++---- .../references/shared/project-layout.md | 17 ++- .../test_bpmn_metadata_refresh_contract.py | 106 ++++++++++++++++++ 8 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 tests/scripts/test_bpmn_metadata_refresh_contract.py diff --git a/.github/workflows/test-helpers.yml b/.github/workflows/test-helpers.yml index ec522c3c41..c27cde58e4 100644 --- a/.github/workflows/test-helpers.yml +++ b/.github/workflows/test-helpers.yml @@ -133,6 +133,22 @@ jobs: - name: Run ScriptTask contract guard run: pytest tests/scripts/test_bpmn_script_task_contract.py -v + bpmn-metadata-refresh-contract-guard: + runs-on: ubuntu-latest + name: maestro-bpmn metadata refresh contract guard + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install pytest + run: pip install pytest + + - name: Run metadata refresh contract guard + run: pytest tests/scripts/test_bpmn_metadata_refresh_contract.py -v + catalog-build-guards: runs-on: ubuntu-latest name: catalog build integrity guards diff --git a/skills/uipath-maestro-bpmn/SKILL.md b/skills/uipath-maestro-bpmn/SKILL.md index c2ae2426c7..353f0dedea 100644 --- a/skills/uipath-maestro-bpmn/SKILL.md +++ b/skills/uipath-maestro-bpmn/SKILL.md @@ -73,7 +73,7 @@ Two halves make a valid Maestro `.bpmn`: ## Workflow -Work the four steps quickly, but keep the path matched to the user's ask. Treat +Work the five steps quickly, but keep the path matched to the user's ask. Treat requests to discover before authoring, save raw registry JSON/evidence, or "do not author yet" as discovery-only even if they describe an eventual BPMN. In that mode, immediately create `registry-evidence/`, but run only the discovery @@ -260,6 +260,31 @@ For registry-evidence-only tasks, be command-first and time-boxed: structural rules, the installed CLI predates them — update it (see [references/cli-conventions.md](references/cli-conventions.md)). See [references/structural-bpmn.md#validation](references/structural-bpmn.md#validation). +5. **Refresh derived metadata when package-ready output is required.** After + source validation passes, regenerate the four CLI-owned package files from + the authored BPMN: + + ```bash + uip maestro bpmn \ + refresh --output json + ``` + + This is the authoritative local source-to-derived-state boundary for + `entry-points.json`, `bindings_v2.json`, `operate.json`, and + `package-descriptor.json`. It is offline and provider-neutral: it does not + log in, discover tenant resources, invoke a connector, or replace + `uip solution resources refresh`. It derives entry points and supported V1 + Connection declarations from identities already authored into the BPMN, + deduplicates shared connection resources, preserves a stable project ID, + and writes the generated set atomically. A second unchanged run is + idempotent. + + Treat a nonzero result as a source/precondition failure. Fix the BPMN or + `project.uiproj`, rerun validation, and refresh again; never repair the four + generated JSON files by hand. If the installed CLI does not expose + `maestro bpmn refresh`, report package generation as blocked rather than + inventing metadata. Refresh is needed only for a package-ready, upload, + debug, publish, or deploy deliverable—not for a source-only draft. ## Operate and diagnose diff --git a/skills/uipath-maestro-bpmn/references/integration-service-activity-authoring-guide.md b/skills/uipath-maestro-bpmn/references/integration-service-activity-authoring-guide.md index 3eb4c32378..e1bb52e516 100644 --- a/skills/uipath-maestro-bpmn/references/integration-service-activity-authoring-guide.md +++ b/skills/uipath-maestro-bpmn/references/integration-service-activity-authoring-guide.md @@ -168,7 +168,9 @@ under different solution resource keys. These root bindings are source model state. `bindings_v2.json`, `entry-points.json`, `operate.json`, and `package-descriptor.json` are generated -package state: never hand-create or edit them. +package state: never hand-create or edit them. After source validation, run +the BPMN `refresh` command to derive the V1 Connection resources and the rest +of the package metadata from the BPMN. ## 5. Serialize request inputs from the described schema diff --git a/skills/uipath-maestro-bpmn/references/operate/CAPABILITY.md b/skills/uipath-maestro-bpmn/references/operate/CAPABILITY.md index c62431593b..c58118ca5f 100644 --- a/skills/uipath-maestro-bpmn/references/operate/CAPABILITY.md +++ b/skills/uipath-maestro-bpmn/references/operate/CAPABILITY.md @@ -32,8 +32,10 @@ These actions may contact UiPath services or external systems. migrate, and cursor movement require a clear user decision for that action. 3. **Validate before operate** - do not upload, publish, debug, or run until Author validation is complete or the user explicitly accepts known draft warnings. -4. **Refresh or regenerate package metadata before cloud actions** - stale `bindings_v2.json`, `entry-points.json`, - `operate.json`, or `package-descriptor.json` can break import or runtime even when the BPMN source is correct. +4. **Refresh package metadata before cloud actions** - after source validation, + run the local BPMN `refresh` command; stale `bindings_v2.json`, + `entry-points.json`, `operate.json`, or `package-descriptor.json` can break + import or runtime even when the BPMN source is correct. 5. **Keep source and package ownership clear** - fix process structure, variables, mappings, events, and documented non-IS extensions in `.bpmn`; rerun CLI generation/enrichment for generated package JSON and Integration Service metadata. 6. **Default publish wording to Studio Web upload unless the user explicitly asks for Orchestrator deployment** - keep deploy semantics explicit. diff --git a/skills/uipath-maestro-bpmn/references/operate/references/ship.md b/skills/uipath-maestro-bpmn/references/operate/references/ship.md index 99c65540bc..f22d79211d 100644 --- a/skills/uipath-maestro-bpmn/references/operate/references/ship.md +++ b/skills/uipath-maestro-bpmn/references/operate/references/ship.md @@ -16,7 +16,13 @@ Before upload, publish, deploy, or debug: and package consistency. 3. Confirm Integration Service enrichment is complete for executable connector elements. If enrichment tooling is unavailable, keep the project as a draft and do not operate it as executable. -4. Regenerate or refresh package metadata with the supported CLI path. +4. Regenerate package metadata from the validated BPMN source: + + ```bash + uip maestro bpmn \ + refresh --output json + ``` + Treat `bindings_v2.json`, `entry-points.json`, `operate.json`, and `package-descriptor.json` as derived unless a CLI contract says otherwise. Use [local-metadata-regeneration-guide.md](../../shared/local-metadata-regeneration-guide.md) for the local drift @@ -46,8 +52,9 @@ uip maestro bpmn pack --output json Use `--name` and `--version` only when the user provides a public-safe package identity. Report the package path and package identity returned by the CLI. -If packing changes generated files, explain whether the change came from BPMN source, CLI enrichment, -or package generation. +Pack should consume the refreshed files rather than own their generation. If +the generated files changed, attribute that change to the preceding BPMN +refresh or enrichment step, not to hand-authored package JSON. ## Studio Web upload @@ -95,7 +102,8 @@ When Studio Web import or packaging fails, inspect the generated package files a - `package-descriptor.json` for content manifest entries under `content/`. If the mismatch comes from process modeling, fix `.bpmn` in Author. -If it comes from connector metadata or generated resources, rerun CLI enrichment/generation. +If it comes from connector metadata or generated resources, fix or re-enrich +the BPMN source and rerun BPMN refresh. Do not hand-patch generated files as the primary fix. ## Failure handling @@ -106,7 +114,8 @@ If package or upload fails: - Check generated package files against [shared/project-layout.md](../../shared/project-layout.md). - Correlate import errors to BPMN diagrams, start events, entry point IDs, bindings, and package descriptor entries. - Return to Author for BPMN/source fixes. -- Rerun CLI enrichment/generation for CLI-owned Integration Service or generated-file issues. +- Fix or re-enrich the BPMN source and rerun BPMN refresh for CLI-owned + Integration Service or generated-file issues. - Use Diagnose only after a process has actually run or faulted in the runtime. ## Anti-patterns diff --git a/skills/uipath-maestro-bpmn/references/shared/local-metadata-regeneration-guide.md b/skills/uipath-maestro-bpmn/references/shared/local-metadata-regeneration-guide.md index 35873082d0..4081825a5e 100644 --- a/skills/uipath-maestro-bpmn/references/shared/local-metadata-regeneration-guide.md +++ b/skills/uipath-maestro-bpmn/references/shared/local-metadata-regeneration-guide.md @@ -2,6 +2,13 @@ Use this guide when BPMN source changed and local package metadata must be refreshed or verified before packaging, upload, debug, publish, or deploy. +The BPMN `refresh` command is the authoritative local source-to-derived-state +boundary. It requires exactly one project-root `.bpmn` file +and atomically regenerates the complete package metadata set. The command is +offline and provider-neutral: it does not log in, discover a tenant, invoke a +connector, or resolve an account. It consumes only identities already authored +into the supported BPMN contract. + ## Ownership - `.bpmn` is the source of record for process structure, root variables, root bindings, entry point IDs, mappings, diagrams, and documented non-Integration-Service UiPath XML. @@ -41,12 +48,12 @@ synthetic package. Local regeneration reads: -- Root-level `bpmn:startEvent` elements with `uipath:entryPointId`. +- Root manual `bpmn:startEvent` elements with `uipath:entryPointId`. - Root `uipath:variables` for entry point input/output schemas. - Root `uipath:bindings` for package resources. - Enriched `uipath:activity` and `uipath:event` payloads for `Intsvc.*` context fields, request payloads, output mappings, and schemas. - The project/start-event path from `operate.json.main` or the selected BPMN - file and root start event. + file and root manual start event. Do not derive metadata from stale package files first. Use existing generated files only as a drift comparison or as CLI-owned enrichment input when the CLI explicitly supports that workflow. @@ -54,33 +61,53 @@ Do not derive metadata from stale package files first. Use existing generated fi 1. Edit `.bpmn` first. 2. Run local validation for XML, diagrams, entry point IDs, variables, mappings, binding references, and package metadata drift. -3. Before running `pack`, verify the project directory contains the full local - metadata set: `project.uiproj`, `operate.json`, `entry-points.json`, - `bindings_v2.json`, and `package-descriptor.json`. The pack command - consumes these files; it does not synthesize a missing package descriptor. -4. If generated package JSON is stale, regenerate it with the supported local - CLI path. Do not substitute hand-written package metadata. For package-shape - verification, use the local pack command and request JSON output when - parsing command results: +3. After validation succeeds, regenerate derived metadata: ```bash - uip maestro bpmn pack --output json + uip maestro bpmn \ + refresh --output json ``` -5. Inspect the package or generated content for: - - `entry-points.json` entries matching root start events and schemas. + A successful response reports the BPMN file, main path, project ID, entry + point count, Connection-resource count, files written, and files unchanged. + The command derives supported V1 Connection resources from root bindings and + `Intsvc.*` activity context, and deduplicates activities that intentionally + share one `resourceKey`. This is connector-provider-neutral; the same source + contract applies regardless of which provider owns the connection. +4. Verify the project now contains the full local metadata set: + `project.uiproj`, `operate.json`, `entry-points.json`, `bindings_v2.json`, + and `package-descriptor.json`. Run refresh a second time only when checking + idempotence; unchanged source must leave all four generated files unchanged. +5. Inspect the generated content for: + - `entry-points.json` entries matching root manual start events and schemas. - `bindings_v2.json` resources matching root bindings and enriched connector metadata. - `operate.json` pointing at the intended BPMN file with `ProcessOrchestration` content type. - `package-descriptor.json` root `files` mappings for the BPMN file and generated JSON. -6. If the installed CLI cannot regenerate a needed file in place, keep the - generated file stale only as a known blocker and report the exact unsupported - step. A source-only project is not package-ready. +6. For package-shape verification, run `pack` only after refresh. Pack consumes + the generated files; it does not synthesize a missing package descriptor: + + ```bash + uip maestro bpmn pack --output json + ``` + +If refresh fails, the atomic write contract leaves the prior four-file set +unchanged. Fix the reported source or project precondition and run it again; do +not patch generated JSON around the failure. The current contract requires +exactly one project-root `.bpmn` file, one or more root processes, and at least +one root manual start event overall. Each root manual start event must carry +exactly one valid GUID `uipath:entryPointId`; refresh generates one +`entry-points.json` entry for each such start event. It rejects unsupported +binding-resource kinds instead of silently dropping them. If the installed CLI +does not expose this command, keep any stale generated files only as known +comparison evidence and report package generation as blocked. A source-only +project is not package-ready. Packaging is local and authoring-safe. Upload, publish, deploy, debug, and run are cloud or runtime actions and still require explicit user consent. ## Entry Point Rules -For each root start event with `uipath:entryPointId`, generated `entry-points.json` must include: +For each root manual start event, require exactly one valid GUID +`uipath:entryPointId`. Its generated `entry-points.json` entry must include: - `uniqueId` equal to the `uipath:entryPointId` value. - `filePath` equal to `/content/#`. @@ -140,6 +167,6 @@ If enrichment is unavailable, leave the BPMN element as draft intent. Do not han ## Drift Handling - If `entry-points.json` differs from root variables or start event IDs, fix the BPMN source first, then regenerate. -- If `bindings_v2.json` differs from root bindings or `Intsvc.*` context references, rerun enrichment/generation. -- If `operate.json` or `package-descriptor.json` points at the wrong BPMN file, refresh package metadata through the CLI path. +- If `bindings_v2.json` differs from root bindings or `Intsvc.*` context references, fix or re-enrich the BPMN source, then run the BPMN refresh command again. +- If `operate.json` or `package-descriptor.json` points at the wrong BPMN file, rerun BPMN refresh instead of editing either file. - Do not commit private IDs, tenant URLs, connection IDs, folder keys, or copied customer payloads while resolving drift. diff --git a/skills/uipath-maestro-bpmn/references/shared/project-layout.md b/skills/uipath-maestro-bpmn/references/shared/project-layout.md index 9c33350190..7a43c4001b 100644 --- a/skills/uipath-maestro-bpmn/references/shared/project-layout.md +++ b/skills/uipath-maestro-bpmn/references/shared/project-layout.md @@ -41,10 +41,19 @@ and leaves it untouched — the project still lands in Treat these JSON files as derived unless a CLI contract explicitly identifies a field as user-authored. For source fixes, edit BPMN or rerun CLI enrichment rather than patching generated output by hand. -Local packaging requires the generated metadata set to exist. In particular, -`uip maestro bpmn pack --output json` consumes -`package-descriptor.json`; it does not create a missing descriptor from only -the BPMN and `project.uiproj`. +After source validation, generate the complete set with: + +```bash +uip maestro bpmn \ + refresh --output json +``` + +Refresh is the offline, provider-neutral source-to-derived boundary. It +regenerates all four files as one atomic set from the authored BPMN and +`project.uiproj`; it does not discover or import tenant resources. Local +packaging then consumes that generated set. In particular, +`uip maestro bpmn pack --output json` does not create +a missing descriptor from only the BPMN and `project.uiproj`. For the regeneration and drift-check contract, see [local-metadata-regeneration-guide.md](local-metadata-regeneration-guide.md). diff --git a/tests/scripts/test_bpmn_metadata_refresh_contract.py b/tests/scripts/test_bpmn_metadata_refresh_contract.py new file mode 100644 index 0000000000..29ede397e9 --- /dev/null +++ b/tests/scripts/test_bpmn_metadata_refresh_contract.py @@ -0,0 +1,106 @@ +"""Guard the BPMN source-to-derived metadata refresh contract.""" + +from __future__ import annotations + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +SKILL_ROOT = ROOT / "skills" / "uipath-maestro-bpmn" +SKILL = SKILL_ROOT / "SKILL.md" +METADATA_GUIDE = ( + SKILL_ROOT / "references" / "shared" / "local-metadata-regeneration-guide.md" +) +PROJECT_LAYOUT = SKILL_ROOT / "references" / "shared" / "project-layout.md" +ACTIVITY_GUIDE = ( + SKILL_ROOT / "references" / "integration-service-activity-authoring-guide.md" +) +SHIP = SKILL_ROOT / "references" / "operate" / "references" / "ship.md" + +REFRESH = "uip maestro bpmn refresh --output json" +VALIDATE = "uip maestro bpmn validate --output json" +PACK = "uip maestro bpmn pack --output json" +GENERATED = ( + "entry-points.json", + "bindings_v2.json", + "operate.json", + "package-descriptor.json", +) + + +def _text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def _prose(text: str) -> str: + return " ".join(text.split()) + + +def _commands(text: str) -> str: + return re.sub(r"\\\s*\n\s*", "", text) + + +def test_skill_orders_validate_before_refresh_and_scopes_it_to_packages() -> None: + text = _text(SKILL) + commands = _commands(text) + + assert "Work the five steps" in text + assert commands.index(VALIDATE) < commands.index(REFRESH) + section = text.split( + "5. **Refresh derived metadata when package-ready output is required.**", + maxsplit=1, + )[1].split("## Operate and diagnose", maxsplit=1)[0] + prose = _prose(section) + assert all(name in section for name in GENERATED) + assert "offline and provider-neutral" in prose + assert "does not log in" in prose + assert "does not expose" in prose + assert "never repair the four generated JSON files by hand" in prose + + +def test_metadata_guide_makes_refresh_the_atomic_source_boundary() -> None: + text = _text(METADATA_GUIDE) + commands = _commands(text) + prose = _prose(text) + + assert REFRESH in commands + assert commands.index("Run local validation") < commands.index(REFRESH) + assert commands.index(REFRESH) < commands.index(PACK) + assert all(name in text for name in GENERATED) + assert "authoritative local" in prose + assert "offline and provider-neutral" in prose + assert "atomically regenerates" in prose + assert "atomic write contract leaves the prior four-file set unchanged" in prose + assert "deduplicates activities" in prose + assert "exactly one project-root `.bpmn` file" in prose + assert "one or more root processes" in prose + assert "at least one root manual start event" in prose + assert "exactly one valid GUID `uipath:entryPointId`" in prose + assert "one `entry-points.json` entry for each such start event" in prose + + +def test_lifecycle_guides_delegate_generation_to_refresh_not_pack() -> None: + layout = _text(PROJECT_LAYOUT) + activity = _text(ACTIVITY_GUIDE) + ship = _text(SHIP) + layout_commands = _commands(layout) + ship_commands = _commands(ship) + layout_prose = _prose(layout) + activity_prose = _prose(activity) + ship_prose = _prose(ship) + + assert REFRESH in layout_commands + assert "source-to-derived boundary" in layout_prose + assert "does not discover or import tenant resources" in layout_prose + assert layout_commands.index(REFRESH) < layout_commands.index( + "uip maestro bpmn pack --output json" + ) + + assert "the BPMN `refresh` command" in activity + assert all(name in activity for name in GENERATED) + assert "never hand-create or edit them" in activity_prose + + assert REFRESH in ship_commands + assert ship_commands.index(REFRESH) < ship_commands.index(PACK) + assert "Pack should consume the refreshed files" in ship_prose