@uipath/skills 1.197.2 → 1.198.0-preview.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CODEOWNERS +18 -11
  4. package/README.md +1 -0
  5. package/assets/skill-status.json +8 -0
  6. package/hooks/hooks.json +44 -8
  7. package/hooks/send-telemetry.ps1 +392 -0
  8. package/hooks/send-telemetry.sh +118 -86
  9. package/hooks/set-session-env.ps1 +72 -0
  10. package/hooks/set-session-env.sh +64 -0
  11. package/hooks/suggest-permissions.ps1 +45 -0
  12. package/hooks/suggest-permissions.sh +4 -0
  13. package/package.json +1 -1
  14. package/skills/uipath-admin/references/audit-commands.md +2 -2
  15. package/skills/uipath-admin/references/audit-workflow-guide.md +4 -4
  16. package/skills/uipath-agents/references/coded/capabilities/batch-transform/planning.md +1 -1
  17. package/skills/uipath-agents/references/coded/capabilities/deeprag/planning.md +1 -1
  18. package/skills/uipath-agents/references/coded/capabilities/guardrails/guardrails-recommend.md +5 -2
  19. package/skills/uipath-agents/references/coded/capabilities/guardrails/guardrails.md +8 -4
  20. package/skills/uipath-agents/references/coded/embedding-in-flows.md +1 -1
  21. package/skills/uipath-agents/references/coded/flow-integration.md +2 -2
  22. package/skills/uipath-agents/references/coded/lifecycle/build.md +0 -1
  23. package/skills/uipath-agents/references/coded/lifecycle/setup.md +0 -1
  24. package/skills/uipath-agents/references/coded/quickstart.md +1 -1
  25. package/skills/uipath-agents/references/lowcode/agent-definition.md +24 -5
  26. package/skills/uipath-agents/references/lowcode/capabilities/built-in-tools/batch-transform/planning.md +1 -1
  27. package/skills/uipath-agents/references/lowcode/capabilities/built-in-tools/deeprag/planning.md +1 -1
  28. package/skills/uipath-agents/references/lowcode/capabilities/context/attachments.md +1 -0
  29. package/skills/uipath-agents/references/lowcode/capabilities/guardrails/guardrails-recommend.md +1 -0
  30. package/skills/uipath-api-workflow/references/cli-reference.md +1 -1
  31. package/skills/uipath-api-workflow/references/connector-activity-discovery.md +1 -1
  32. package/skills/uipath-api-workflow/references/troubleshooting.md +1 -1
  33. package/skills/uipath-automation-discovery/SKILL.md +26 -1
  34. package/skills/uipath-automation-discovery/assets/templates/estimation-worksheet-template.md +80 -0
  35. package/skills/uipath-automation-discovery/references/estimation-guide.md +104 -0
  36. package/skills/uipath-connector-builder/SKILL.md +6 -4
  37. package/skills/uipath-connector-builder/references/auth.md +137 -13
  38. package/skills/uipath-connector-builder/references/configuration.md +29 -7
  39. package/skills/uipath-connector-builder/references/debugging.md +1 -1
  40. package/skills/uipath-connector-builder/references/standard-resources.md +162 -13
  41. package/skills/uipath-governance/references/cli-cheatsheet.md +3 -3
  42. package/skills/uipath-governance/references/diagnose/references/failure-modes.md +5 -5
  43. package/skills/uipath-insights/SKILL.md +277 -0
  44. package/skills/uipath-insights/references/investigation-playbook-guide.md +145 -0
  45. package/skills/uipath-insights/references/jobs-commands-guide.md +144 -0
  46. package/skills/uipath-ixp/SKILL.md +4 -1
  47. package/skills/uipath-ixp/references/cli-reference.md +18 -3
  48. package/skills/uipath-maestro-bpmn/references/cli-conventions.md +2 -0
  49. package/skills/uipath-maestro-bpmn/validator/README.md +12 -2
  50. package/skills/uipath-maestro-case/SKILL.md +27 -13
  51. package/skills/uipath-maestro-case/assets/templates/sdd-template-examples.md +37 -4
  52. package/skills/uipath-maestro-case/assets/templates/sdd-template.md +20 -9
  53. package/skills/uipath-maestro-case/assets/templates/sdd-viewer.html +8 -7
  54. package/skills/uipath-maestro-case/references/bindings-v2-sync.md +5 -1
  55. package/skills/uipath-maestro-case/references/brownfield.md +86 -0
  56. package/skills/uipath-maestro-case/references/case-commands.md +8 -0
  57. package/skills/uipath-maestro-case/references/case-editing-operations.md +230 -21
  58. package/skills/uipath-maestro-case/references/case-schema.md +29 -11
  59. package/skills/uipath-maestro-case/references/connector-integration.md +1 -1
  60. package/skills/uipath-maestro-case/references/connector-trigger-common.md +1 -1
  61. package/skills/uipath-maestro-case/references/entry-points-sync.md +204 -0
  62. package/skills/uipath-maestro-case/references/implementation.md +19 -9
  63. package/skills/uipath-maestro-case/references/phase-0-interview.md +10 -8
  64. package/skills/uipath-maestro-case/references/phased-execution.md +11 -4
  65. package/skills/uipath-maestro-case/references/placeholder-tasks.md +24 -2
  66. package/skills/uipath-maestro-case/references/planning.md +9 -3
  67. package/skills/uipath-maestro-case/references/plugins/case/planning.md +2 -0
  68. package/skills/uipath-maestro-case/references/plugins/sla/impl-json.md +1 -1
  69. package/skills/uipath-maestro-case/references/plugins/sla/planning.md +1 -1
  70. package/skills/uipath-maestro-case/references/plugins/stages/impl-json.md +11 -10
  71. package/skills/uipath-maestro-case/references/plugins/stages/planning.md +13 -14
  72. package/skills/uipath-maestro-case/references/plugins/tasks/agent/impl-json.md +3 -1
  73. package/skills/uipath-maestro-case/references/plugins/tasks/agent/planning.md +91 -2
  74. package/skills/uipath-maestro-case/references/plugins/tasks/api-workflow/impl-json.md +3 -1
  75. package/skills/uipath-maestro-case/references/plugins/tasks/api-workflow/planning.md +87 -2
  76. package/skills/uipath-maestro-case/references/plugins/tasks/connector-activity/planning.md +1 -1
  77. package/skills/uipath-maestro-case/references/plugins/triggers/event/impl-json.md +1 -1
  78. package/skills/uipath-maestro-case/references/plugins/triggers/manual/impl-json.md +3 -1
  79. package/skills/uipath-maestro-case/references/plugins/triggers/timer/impl-json.md +2 -1
  80. package/skills/uipath-maestro-case/references/plugins/variables/bindings/impl-json.md +2 -0
  81. package/skills/uipath-maestro-case/references/plugins/variables/global-vars/impl-json.md +20 -10
  82. package/skills/uipath-maestro-case/references/plugins/variables/global-vars/planning.md +9 -8
  83. package/skills/uipath-maestro-case/references/plugins/variables/io-binding/impl-json.md +1 -1
  84. package/skills/uipath-maestro-case/references/plugins/variables/io-binding/planning.md +1 -1
  85. package/skills/uipath-maestro-case/references/registry-discovery.md +87 -10
  86. package/skills/uipath-maestro-case/references/sdd-generation-rules.md +34 -26
  87. package/skills/uipath-maestro-flow/SKILL.md +4 -1
  88. package/skills/uipath-maestro-flow/references/author/CAPABILITY.md +1 -2
  89. package/skills/uipath-maestro-flow/references/author/references/editing-operations-json.md +3 -3
  90. package/skills/uipath-maestro-flow/references/author/references/greenfield.md +4 -2
  91. package/skills/uipath-maestro-flow/references/author/references/planning-arch.md +0 -1
  92. package/skills/uipath-maestro-flow/references/author/references/planning-impl.md +0 -1
  93. package/skills/uipath-maestro-flow/references/author/references/plugins/connector/impl.md +1 -0
  94. package/skills/uipath-maestro-flow/references/author/references/plugins/connector-trigger/impl.md +7 -7
  95. package/skills/uipath-maestro-flow/references/author/references/plugins/ixp/impl.md +14 -14
  96. package/skills/uipath-maestro-flow/references/author/references/plugins/script/impl.md +10 -6
  97. package/skills/uipath-maestro-flow/references/shared/cli-conventions.md +7 -0
  98. package/skills/uipath-maestro-flow/references/shared/file-format.md +1 -1
  99. package/skills/uipath-mcp-servers/references/is-activity-workflow.md +3 -3
  100. package/skills/uipath-planner/SKILL.md +1 -1
  101. package/skills/uipath-platform/SKILL.md +4 -14
  102. package/skills/uipath-platform/references/data-fabric/data-fabric.md +1 -1
  103. package/skills/uipath-platform/references/integration-service/agent-workflow.md +4 -4
  104. package/skills/uipath-platform/references/integration-service/reference-resolution.md +1 -1
  105. package/skills/uipath-platform/references/integration-service/resources.md +5 -5
  106. package/skills/uipath-platform/references/integration-service/triggers.md +10 -9
  107. package/skills/uipath-platform/references/orchestrator/setup-environment.md +2 -0
  108. package/skills/uipath-review/SKILL.md +8 -4
  109. package/skills/uipath-review/references/agents/agent-review-checklist.md +1 -22
  110. package/skills/uipath-review/references/agents/agents-coded-rules.md +24 -7
  111. package/skills/uipath-review/references/agents/agents-lowcode-rules.md +0 -1
  112. package/skills/uipath-review/references/agents/guardrails/coded-guardrails-review.md +231 -0
  113. package/skills/uipath-review/references/rule-catalog-workflow.md +4 -2
  114. package/skills/uipath-rpa/SKILL.md +2 -2
  115. package/skills/uipath-rpa/references/activity-docs/UiPath.DataService.Activities/guides/data-service-filter-builder-guide.md +1 -1
  116. package/skills/uipath-rpa/references/activity-docs/UiPath.Web.Activities/2.5/coded/coded-api.md +1 -1
  117. package/skills/uipath-rpa/references/activity-docs/UiPath.Web.Activities/2.5/coded/curl-import.md +3 -3
  118. package/skills/uipath-rpa/references/activity-docs/UiPath.Web.Activities/2.5/coded/http-request-upgrade.md +3 -3
  119. package/skills/uipath-rpa/references/activity-docs/UiPath.Web.Activities/2.5/coded/service-discovery.md +2 -2
  120. package/skills/uipath-rpa/references/coded/coding-guidelines.md +1 -1
  121. package/skills/uipath-rpa/references/ui-automation-guide.md +7 -7
  122. package/skills/uipath-rpa/references/uia-configure-target-workflows.md +5 -5
  123. package/skills/uipath-rpa/references/uia-elements-interaction-guide.md +19 -10
  124. package/skills/uipath-rpa/references/uia-prerequisites.md +7 -7
  125. package/skills/uipath-rpa/references/validation-guide.md +2 -2
  126. package/skills/uipath-rpa/references/xaml/common-pitfalls.md +4 -0
  127. package/skills/uipath-rpa/references/xaml/csharp-activity-binding-guide.md +3 -1
  128. package/skills/uipath-rpa/references/xaml/csharp-expression-pitfalls.md +9 -4
  129. package/skills/uipath-rpa/references/xaml/workflow-guide.md +2 -2
  130. package/skills/uipath-rpa/references/xaml/xaml-basics-and-rules.md +1 -1
  131. package/skills/uipath-solution/SKILL.md +4 -4
  132. package/skills/uipath-solution/references/activate-and-manage.md +6 -1
  133. package/skills/uipath-solution/references/develop-solution.md +24 -5
  134. package/skills/uipath-solution/references/pack-and-deploy.md +5 -2
  135. package/skills/uipath-solution/references/scenarios/manual-edits.md +2 -2
  136. package/skills/uipath-solution/references/solution-overview.md +2 -2
  137. package/skills/uipath-troubleshoot/SKILL.md +3 -3
  138. package/skills/uipath-troubleshoot/references/products/agents/playbooks/context-grounding-index-not-found.md +0 -2
  139. package/skills/uipath-troubleshoot/references/products/maestro/playbooks/personal-automation-quota.md +1 -1
  140. package/version-manifest.json +3 -3
  141. package/hooks/ensure-uip.sh +0 -220
  142. package/skills/uipath-maestro-flow/references/author/references/plugins/connector/data-fabric/impl.md +0 -683
  143. package/skills/uipath-maestro-flow/references/author/references/plugins/connector/data-fabric/planning.md +0 -45
@@ -96,9 +96,9 @@ Rationale: the formal In-arg slot id surfaces in the case BPMN as `<uipath:input
96
96
 
97
97
  ## Inputs the plugin reads at Phase 3 Step 6.2
98
98
 
99
- 1. **`tasks.md`** variable T-entries — for category, type, default, sourceTrigger(s), sourceField(s)
99
+ 1. **`tasks.md`** variable T-entries — for category, type, default, sourceTrigger(s), sourceField(s). On `Category=In` rows, `sourceTriggers` is a single T-number selecting the bound trigger (blank → primary trigger)
100
100
  2. **`tasks/trigger-spec-cache.json`** — for each trigger's `caseShape.outputs[]` (un-minted), keyed by T-number. Written by trigger plugin at Step 6.1; see [`../../triggers/event/impl-json.md` § Step 8](../../triggers/event/impl-json.md) for the writer-side schema. Top-level keys are T-numbers (e.g., `T02`, `T03`); values have `context`, `inputs`, `outputs` from the trigger's `caseShape`, un-minted (no `var` / `id` / `elementId` synthesized).
101
- 3. **`id-map.json`** — for `T<N> → trigger_xxxxxx` lookup when writing trigger.outputs[]
101
+ 3. **`id-map.json`** — for `T<N> → trigger_xxxxxx` lookup when writing trigger.outputs[] and resolving an `In`-arg's bound trigger node (row's `sourceTriggers`; blank → `id-map["T02"].id`, the primary trigger)
102
102
  4. **`caseplan.json`** — to locate trigger nodes (by triggerId from id-map) and existing root variable arrays
103
103
 
104
104
  ## Dispatcher — two loops
@@ -116,7 +116,7 @@ For each trigger in `trigger-spec-cache.json`:
116
116
  |---|---|---|---|---|---|
117
117
  | Not referenced by SDD | (no row) | `{name: <spec.name>, var: <spec.name>, type: <spec.type>, source: <spec.source>, value: <spec.name>}` — `type` and `source` come from the spec entry verbatim (e.g., `type: "jsonSchema"` + `source: "=response"`). **No `id`, no `elementId`** per FE auto-emit convention (`IntsvcActivityPropertiesUtils.tsx:288-302`). Plain-name auto-emit. | — | — | **Required** — `{id: <spec.name>, name: <spec.name>, type: <spec.type>, elementId: <triggerId>, body: <spec.body>}`. For `jsonSchema`-typed entries (e.g., `response`, `Error`), the companion holds the full body schema that the FE picker uses to discover sub-fields. Without it, sub-field picking is broken and the variable can't be selected in connector-task input bindings. |
118
118
  | Referenced as `Category=Variable` | row's `sourceField` path | `{name: <last segment of sourceField path>, var: <sdd-name>, type: <sdd-row.type>, source: "=<row.sourceField>", value: <sdd-name>}` (Pattern C wire). **No `id`, no `elementId`** — resolution flows through the companion in `root.inputOutputs[]`, not through this entry. `name` is the spec sub-field segment (e.g., `"Title"` when `sourceField: response.Title`) — matches FE convention where `name` is the display label of the source field. `source` is `=` prepended to the raw `sourceField` value from tasks.md; `type` comes from the SDD row, NOT the spec — author's chosen type wins. | — | — | `{id: <sdd-name>, name: <sdd-name>, type: <sdd-row.type>, elementId: "root", custom: true}` — companion with elementId="root" routes variable to Case Variables panel; `custom: true` marks it user-declared. |
119
- | Referenced as `Category=In` | — (In doesn't reference a payload field; the value comes from caller or Default at fire) | Bridge entry per § In argument below `{name: <sdd-name>, type: <sdd-row.type>, source: "=vars.<inputId>", var: <sdd-name>}`. Works on ANY trigger type (manual, timer, event). For event triggers, the bridge propagates the formal slot's `default` to the companion at trigger fire (no caller-override path, but mechanics are identical). | per § In argument below | — | per § In argument below |
119
+ | Referenced as `Category=In` | **Skip here** `Category=In` is dispatched in Loop B by Category, NEVER by spec-output name-match (even if an In-arg's Name happens to equal a top-level spec name). The bridge + slot + companion bind to the row's `sourceTriggers` trigger (blank primary T02), not the trigger Loop A is iterating. See Loop B + § In argument. | — (emit nothing) | | | |
120
120
  | Referenced as `Category=Out` | — | **REJECT** (direction mismatch — Out-args flow case→caller) | — | — | — |
121
121
 
122
122
  **File-type carve-out:** when the spec output's `type` is `"file"` (or `"octet-stream"` — normalize to `"file"`), both rows above additionally require:
@@ -148,9 +148,11 @@ root.inputOutputs[]: [
148
148
 
149
149
  Six entries total: 3 trigger outputs (no id) + 3 companions (with id — resolution surface). The auto-emit companions carry the full body schemas so the FE picker can navigate sub-fields. The Pattern C companion has no body (its type is the primitive `string`).
150
150
 
151
- ### Loop B — SDD-only rows (rows with no trigger source)
151
+ ### Loop B — non-extraction rows (In / Out / pure-state Variable)
152
152
 
153
- For each variable T-entry in `tasks.md` that has **no `sourceTrigger` / `sourceTriggers` field**:
153
+ For each variable T-entry in `tasks.md` that is **`Category=In`** (any `sourceTriggers` — single or blank), **`Category=Out`**, or a **`Category=Variable`** row with **no `sourceTrigger` / `sourceTriggers` field**:
154
+
155
+ > **`Category=In` always lands here** — even when it carries a `sourceTriggers` T-number. An In-arg names its bound trigger but extracts no payload field, so it is NEVER a Loop A (Pattern C) row. Only `Variable` rows with `sourceTrigger(s)` go to Loop A.
154
156
 
155
157
  | SDD row | `root.inputs[]` | `root.outputs[]` | `root.inputOutputs[]` |
156
158
  |---|---|---|---|
@@ -159,7 +161,9 @@ For each variable T-entry in `tasks.md` that has **no `sourceTrigger` / `sourceT
159
161
  | `Category=Out` (companion ALWAYS emitted — see § Out argument) | — | `{id: v<random8>, name: <sdd-name>, type: <type>, var: <sdd-name>}` (formal-arg pointer) | `{id: <sdd-name>, name: <sdd-name>, type: <type>, default: <value or "">, elementId: "root"}` (no `custom`) |
160
162
  | `Category=InOut` | (not supported in v1 — see SDD template) | (not supported in v1) | (not supported in v1) |
161
163
 
162
- > Loop A and Loop B can write the SAME `root.inputOutputs[]` entry when an SDD row appears in both contexts (e.g., a `Category=Variable` row with `sourceTrigger`). Apply dedup by `id`: if an entry with the same `id` already exists from Loop A, do not re-write in Loop B; Phase 2 validator has already confirmed there's no Type/Default conflict.
164
+ > **`<triggerId>` for a `Category=In` row** resolves to `id-map.json[T<N>].id` where `T<N>` is the row's `sourceTriggers` (a single T-number); blank `sourceTriggers` the primary trigger `id-map["T02"].id`. The formal slot, companion, and bridge all attach to that one trigger node. Full 3-entry shape: § In argument.
165
+ >
166
+ > Loop A and Loop B can write the SAME `root.inputOutputs[]` entry when a `Category=Variable` row appears in both contexts (e.g., a `Variable` with `sourceTrigger`). Apply dedup by `id`: if an entry with the same `id` already exists from Loop A, do not re-write in Loop B; Phase 2 validator has already confirmed there's no Type/Default conflict. `Category=In` rows never straddle the loops — they are emitted solely in Loop B with the In-arg 3-entry shape, never a Pattern-C entry.
163
167
 
164
168
  ## Pattern shapes by category
165
169
 
@@ -218,7 +222,11 @@ Resolver doesn't care that two trigger entries write to the same `vars.caseStart
218
222
 
219
223
  ### In argument
220
224
 
221
- SDD row: `Category=In`, `triggerRef: T02`. **Works on any trigger type — manual, timer, or event.** For event triggers, the bridge mechanics are identical; the formal slot's `default` propagates to the companion at trigger fire (no caller-override path since events have no API caller, but the structural emission is the same).
225
+ SDD row: `Category=In`, optional `sourceTriggers: T<N>` (a single T-number selecting the bound trigger; blank → primary trigger T02). `sourceFields` is empty for In rows. **Works on any trigger type — manual, timer, or event.** For event triggers, the bridge mechanics are identical; the formal slot's `default` propagates to the companion at trigger fire (no caller-override path since events have no API caller, but the structural emission is the same).
226
+
227
+ **Trigger resolution.** `<triggerId>` in the entries below = `id-map.json[T<N>].id` for the trigger named by `sourceTriggers`; blank `sourceTriggers` → `id-map["T02"].id` (the primary trigger). Entries 1 (formal slot) and 2 (companion) carry it as `elementId`; entry 3 (bridge) is written on that same trigger node's `data.uipath.outputs[]`. `sourceFields` is not consulted for In.
228
+
229
+ > **Bare manual bound trigger:** a manual trigger has no `data.uipath` key (its signature — see [`../../triggers/manual/impl-json.md`](../../triggers/manual/impl-json.md)). When the bound trigger is manual (the common case — the primary trigger is usually manual), create `data.uipath = { "outputs": [] }` on that node before appending the bridge. Do NOT add a `serviceType` — its absence is what keeps the trigger manual.
222
230
 
223
231
  Three entries — formal slot + companion + bridge:
224
232
 
@@ -238,7 +246,7 @@ Three entries — formal slot + companion + bridge:
238
246
 
239
247
  **Why three entries instead of one?** The runtime resolver (`VariablesService.findVariableByVariableId`) is a single string-equality find on `Variable.id`. The caller (or trigger fire for event triggers) writes the formal-arg's value into `vars.v<random8>` at trigger fire (because `inputs[].id` is `v<random8>`); downstream code wants to read it as `=vars.applicantName` (because that's the readable name). There is no automatic forwarding between the two slots — the bridge entry on `triggerNode.outputs[]` executes the copy at fire time: `source: "=vars.v<random8>"` reads the formal slot, `var: "applicantName"` writes to the companion's slot. Without the bridge, `=vars.applicantName` resolves to undefined. The companion's `inputOutputs[]` entry alone declares the *name* in the namespace, but holds no *value* because nobody writes to it.
240
248
 
241
- > **Placeholder trigger interaction:** if the trigger is a placeholder (any type), write entries 1 + 2 only; skip the bridge (entry 3) — the placeholder has no `data.uipath.outputs` array. The placeholder trigger never fires, so the bridge would never execute anyway. **Consequence:** at runtime `vars.<name>` (the companion slot) is undefined — the `default` on the `inputs[]` formal slot does NOT propagate to the companion without the bridge. This is expected: a placeholder case is structurally incomplete and not meant to run until the trigger is resolved. Re-generate from scratch (Rule 6) after the trigger resolves to get the working bridge.
249
+ > **Placeholder trigger interaction:** if the **bound trigger** (the one named by `sourceTriggers`, or the primary trigger when blank) is a placeholder (any type), write entries 1 + 2 only; skip the bridge (entry 3) — the placeholder has no `data.uipath.outputs` array. The placeholder trigger never fires, so the bridge would never execute anyway. **Consequence:** at runtime `vars.<name>` (the companion slot) is undefined — the `default` on the `inputs[]` formal slot does NOT propagate to the companion without the bridge. This is expected: a placeholder case is structurally incomplete and not meant to run until the trigger is resolved. Re-generate from scratch (Rule 6) after the trigger resolves to get the working bridge.
242
250
 
243
251
  **File-type In-arg carve-out:** when `type === "file"`:
244
252
  - Formal slot (entry 1) MUST add `body: <FILE_TYPE_JSON_SCHEMA>` (see [`## file type`](#file-type)) — drives entry-points.json `$ref: "#/definitions/job-attachment"` at packaging
@@ -277,7 +285,7 @@ Same shape regardless of Default presence (two entries):
277
285
 
278
286
  ### InOut argument
279
287
 
280
- > **NOT supported in v1.** The SDD template's Category enum is `In | Out | Variable` only. If real customer cases require InOut, this section documents the FE-canonical shape for future re-introduction. Skill validators reject `Category=InOut` in v1 (Phase 2 — invalid Category value).
288
+ > **NOT supported in v1.** The SDD template's Category enum is `In | Out | Variable` only. If real customer cases require InOut, this section documents the FE-canonical shape for future re-introduction. Skill validators reject `Category=InOut` in v1 (Phase 2 — invalid Category value). If revived, `<triggerId>` resolves per § In argument (`id-map[<sourceTriggers T-number>].id`; blank → `id-map["T02"].id`).
281
289
 
282
290
  Combines In + Out. One shared companion serves both:
283
291
 
@@ -316,6 +324,8 @@ All logged per [`../../logging/impl-json.md`](../../logging/impl-json.md).
316
324
  > |---|---|---|
317
325
  > | Category column missing or empty | Phase 2 (planning) | SDD-only structural check; needs no spec data |
318
326
  > | `Category=Out` + `sourceTriggers` declared | Phase 2 | Direction mismatch is purely SDD-internal |
327
+ > | `Category=In` + CSV `sourceTriggers` (>1 T-number) | Phase 2 | In binds exactly one trigger; SDD-internal |
328
+ > | `Category=In` + non-empty `sourceFields` | Phase 2 | In extracts no payload field; SDD-internal |
319
329
  > | Same-Name pair (any column mismatch) | Phase 2 | Pure SDD consistency check; not re-validated in Phase 3 |
320
330
  > | Missing `Type` on In/Out row | Phase 2 | SDD-internal |
321
331
  > | `sourceTriggers` references nonexistent T-number | Phase 2 | tasks.md cross-reference, no spec needed |
@@ -326,7 +336,7 @@ All logged per [`../../logging/impl-json.md`](../../logging/impl-json.md).
326
336
  >
327
337
  > Phase 3 does NOT re-validate the Phase 2 structural checks — they are prerequisite-met by the time Phase 3 runs (Phase 2 rejects before tasks.md is finalized).
328
338
  >
329
- > **`Category=In` on event triggers is ALLOWED** in v1 (per SDD contract). The previous rejection rule for Category=In on event triggers is removed — the structural emission for In (3-entry: formal slot + companion + bridge) is identical regardless of trigger type. For event triggers, the formal slot's `default` propagates through the bridge to the companion at trigger fire; there's no caller-override path, but the mechanics are valid.
339
+ > **`Category=In` on event triggers is ALLOWED** in v1 (per SDD contract). The previous rejection rule for Category=In on event triggers is removed — the structural emission for In (3-entry: formal slot + companion + bridge) is identical regardless of trigger type. For event triggers, the formal slot's `default` propagates through the bridge to the companion at trigger fire; there's no caller-override path, but the mechanics are valid. The bound trigger is the one named by `sourceTriggers` (blank → primary) and may be any type.
330
340
 
331
341
  ## Custom Outputs (`custom: true` on task.data.outputs[])
332
342
 
@@ -5,7 +5,7 @@ Case-level data lives in the top-level `variables` block. Three categories:
5
5
  | Category | Arrays touched | When |
6
6
  |---|---|---|
7
7
  | **Variable** | `inputOutputs[]` (declaration) + `triggerNode.outputs[]` (when populated by trigger payload) | Case-internal state, including trigger-payload-sourced state |
8
- | **In** | `inputs[]` + companion `inputOutputs[]` + `triggerNode.outputs[]` bridge | Formal argument supplied by external caller. Any trigger type (manual, timer, or event). |
8
+ | **In** | `inputs[]` + companion `inputOutputs[]` + `triggerNode.outputs[]` bridge | Formal argument supplied by external caller. Binds to the trigger named by `sourceTriggers` (single `T<N>`; blank → primary trigger T02). Any trigger type (manual, timer, or event). |
9
9
  | **Out** | `outputs[]` + companion `inputOutputs[]` (ALWAYS — see [`impl-json.md` § Out argument](impl-json.md)) | Formal argument returned to caller at case end |
10
10
 
11
11
  > **Canonical definition:** [`impl-json.md` § Pattern shapes by category](impl-json.md) is the source of truth for emission shapes (which arrays get written, exact JSON shape, runtime resolution semantics). This table is the Phase 1 routing summary.
@@ -43,7 +43,9 @@ Validate at planning time (before tasks.md is finalized). All checks operate on
43
43
  | `Category=In` or `Out` row has missing `Type` | ERROR | Reject — type is required for formal arguments. |
44
44
  | Two rows share the same `Name` (regardless of which other columns differ) | ERROR | Reject — name collision. Variable names MUST be globally unique. AskUserQuestion to resolve (rename one, or merge into one row). |
45
45
  | `Category=Variable` row has `sourceTriggers` but no matching `sourceFields` entry per trigger | ERROR | Reject — multi-trigger requires per-trigger sourceField. |
46
- | `sourceTriggers` references a T-number that doesn't exist in tasks.md | ERROR | Reject — orphan reference. |
46
+ | `Category=In` row has CSV `sourceTriggers` (more than one T-number) | ERROR | Reject — an In-arg binds to exactly one trigger; CSV is the multi-trigger `Variable` form. |
47
+ | `Category=In` row has non-empty `sourceFields` | ERROR | Reject — In-args select a trigger but extract no payload field; use `Category=Variable` for extraction. |
48
+ | `sourceTriggers` references a T-number that doesn't exist in tasks.md (any category) | ERROR | Reject — orphan reference. |
47
49
 
48
50
  Phase 3 (implementation) catches spec-dependent issues — see [`impl-json.md`](impl-json.md) § Phase 3 Validation.
49
51
 
@@ -55,9 +57,9 @@ One T-entry per Case Variables row. Place after the case file (T01) and all trig
55
57
  ## T05: Declare In-argument "applicantName"
56
58
  - category: In
57
59
  - type: string
58
- - triggerRef: T02
60
+ - sourceTriggers: T03 # single T-number; omit to bind the primary trigger (T02)
59
61
  - default: ""
60
- - verify: inputs[] formal slot + inputOutputs[] companion (elementId=<triggerId>) + triggerNode.outputs[] bridge written.
62
+ - verify: inputs[] formal slot + inputOutputs[] companion (elementId = id-map[T03].id) + that trigger node's outputs[] bridge written.
61
63
 
62
64
  ## T06: Declare Variable "subject"
63
65
  - category: Variable
@@ -92,16 +94,15 @@ One T-entry per Case Variables row. Place after the case file (T01) and all trig
92
94
 
93
95
  - `category` — required, one of `In`, `Out`, `Variable`
94
96
  - `type` — required, one of `string`, `integer`, `float`, `double`, `boolean`, `datetime`, `date`, `jsonSchema`, `file`
95
- - `triggerRef` — T-number of the trigger this In-arg is attached to (single-trigger). For In-args only.
96
97
  - `sourceTrigger` — T-number when the value comes from a single trigger's payload (Variable category)
97
- - `sourceTriggers` — CSV of T-numbers when multiple triggers populate this Variable
98
- - `sourceFields` — per-trigger payload paths. Single-trigger form is `<path>`; multi-trigger form is a YAML-style sub-block with one `T<N>: <path>` per line
98
+ - `sourceTriggers` — for a `Variable`: CSV of T-numbers when multiple triggers populate it. For an `In`-arg: a single `T<N>` selecting the trigger it binds to (blank → primary trigger T02; never a CSV). Replaces the legacy `triggerRef` field.
99
+ - `sourceFields` — per-trigger payload paths (Variable only). Single-trigger form is `<path>`; multi-trigger form is a YAML-style sub-block with one `T<N>: <path>` per line. Empty on `In` rows.
99
100
  - `default` — initial value (string-encoded for non-string types). Drives the `default` field on the companion `inputOutputs[]` entry.
100
101
  - `producedBy` — informational only (for Out-args). The io-binding validator confirms the named task actually exists with a matching output.
101
102
 
102
103
  **`verify` text — use exact terms from [`impl-json.md` § Pattern shapes](impl-json.md):**
103
104
 
104
- - "Bridge" = In-arg formal-arg → companion forwarding (any trigger type; 3-entry shape). NEVER use for Variable rows.
105
+ - "Bridge" = In-arg formal-arg → companion forwarding (any trigger type; 3-entry shape) on the trigger named by the In row's `sourceTriggers` (blank → primary). NEVER use for Variable rows.
105
106
  - Variable-row trigger.outputs[] entries are "Pattern C wires" (direct payload extraction, 2-entry shape).
106
107
  - `sourceField`'s right side IS the connector's spec path (e.g., `response.subject` is the literal field path in `caseShape.outputs[]`), not an alias. SDD-name on the LEFT becomes `var`/`id`; spec path on the RIGHT becomes `source`.
107
108
  - Spec-vs-SDD drift validation runs in the variables plugin's Phase 3 dispatcher, not in io-binding.
@@ -110,7 +110,7 @@ for each string value V anywhere in caseplan.json:
110
110
  write V back
111
111
  ```
112
112
 
113
- Resolution semantics are identical to whole-value `<-` (same name-triple, same lookup), with two differences: the substitution is **bare** `vars.<var>` (the marker already sits inside `=js:`), and it happens in a global string pass rather than against a single input's `value`. Exception-stage / adhoc scoping (reference any task across any stage) applies unchanged.
113
+ Resolution semantics are identical to whole-value `<-` (same name-triple, same lookup), with two differences: the substitution is **bare** `vars.<var>` (the marker already sits inside `=js:`), and it happens in a global string pass rather than against a single input's `value`. Secondary-stage / adhoc scoping (reference any task across any stage) applies unchanged.
114
114
 
115
115
  After this pass and all bindings, run the end-of-Phase-3 validator. It performs the cross-reference checks below:
116
116
 
@@ -76,5 +76,5 @@ Apply at planning time (Phase 2):
76
76
  | Task location | Can reference |
77
77
  |---|---|
78
78
  | Regular stage task | Earlier stages + same stage earlier tasks + root variables |
79
- | Exception stage task | ALL tasks across ALL stages |
79
+ | Secondary stage task | ALL tasks across ALL stages |
80
80
  | Adhoc task | ALL tasks |
@@ -12,7 +12,7 @@ During sdd.md → task.md interpretation, when you need to determine:
12
12
 
13
13
  Run `uip maestro case registry pull` before any lookups. This populates the local cache at `~/.uip/case-resources/`. All subsequent discovery is done by reading these cache files directly — **do not** rely on `uip maestro case registry search` as the primary discovery method. See the "CLI Search Gaps" section below for the reason.
14
14
 
15
- > **Missing file ≠ empty match.** Before searching any `<type>-index.json`, verify it exists on disk. If it does not, run `uip maestro case registry pull` (not `--force` — a normal pull is enough for first-time population). The Rule 17 / § MUST-Confirm-Before-Placeholder-Fallback gate only applies to **empty matches inside an existing cache**; a missing file is a precondition failure, not a 0-result lookup. If the file is still absent after a successful pull, the tenant has no resources of that type proceed to placeholder.
15
+ > **Missing file ≠ empty match.** Before searching any `<type>-index.json`, verify it exists on disk. If it does not, run `uip maestro case registry pull` (not `--force` — a normal pull is enough for first-time population). A missing file **before** a pull is a precondition failure, not a 0-result lookup. **After a successful pull, a still-absent index means the tenant has zero resources of that type — which IS the genuine 0-matches case.** For **non-creatable** types (regular RPA process, agentic processes / Process Orchestration, action, connectors) → proceed to placeholder. For a **creatable** type (`agent`, `api-workflow`), a zero-resource tenant index is the genuine 0-matches case where inline **Create** applies — but **first resolve any in-solution sibling** (a prior run may have already built it; see the per-type pre-gate checks — [agent/planning.md](plugins/tasks/agent/planning.md#registry-resolution), [api-workflow/planning.md](plugins/tasks/api-workflow/planning.md#registry-resolution) and § Handle Empty Results below). Only a resource absent from **both** the tenant index **and** the local siblings is genuinely empty → feed it to the [Rule 17 / § MUST-Confirm gate](#must-confirm-before-placeholder-fallback) (Create offered), NOT straight to placeholder.
16
16
 
17
17
  ## CLI Search Gaps
18
18
 
@@ -32,17 +32,21 @@ Required prompt shape:
32
32
 
33
33
  ```
34
34
  Question: <N> registry lookup(s) returned 0 matches: <comma-list of <name> in <folder>>.
35
- Run `uip maestro case registry pull --force` to bypass the cache and re-resolve?
36
- Header: Force pull
35
+ Header: Resolve empties
37
36
  Options:
38
- - Yes, force pull and re-resolve
39
- → run `uip maestro case registry pull --force`, re-search caches, update registry-resolved.json with the second-pass results.
40
- Any STILL-empty lookups go to placeholder ONLY after this round.
37
+ - Force pull and re-resolve
38
+ → run `uip maestro case registry pull --force`, re-search caches, update registry-resolved.json with the
39
+ second-pass results, then LOOP BACK to this prompt for any STILL-empty lookup.
40
+ - Create the missing resource(s) inline # shown ONLY when ≥1 still-empty is an `agent` or `api-workflow` AND the CLI has `registry --local`
41
+ → multi-select which to build as in-solution siblings (agent → uipath-agents,
42
+ api-workflow → uipath-api-workflow); build them (§ Create-on-Missing). Unselected items
43
+ + all non-creatable empties (regular RPA process, action, connectors, agentic
44
+ processes) → placeholder.
41
45
  - Skip and use placeholders
42
46
  → proceed to per-plugin Unresolved Fallback paths for the unmatched lookups.
43
47
  ```
44
48
 
45
- **Apply once per planning batch, not per-task.** A single prompt covers every empty in that batch.
49
+ **Apply once per planning batch, not per-task.** Each option is batch-level — never a per-task yes/no chain. Force pull loops back to this same prompt for whatever stays empty. The **Create** option covers **agents and API workflows only** (never regular RPA process, action apps, connectors, or agentic processes); it appears only when ≥1 still-empty is creatable AND the CLI supports `registry --local` (capability probe — see [§ Create-on-Missing](#create-on-missing-build-and-rediscovery)). When `--local` is absent the gate degrades to Force pull / Skip exactly as before.
46
50
 
47
51
  **Do NOT pre-judge.** Resource-name heuristics ("looks vendor-specific, won't be in registry anyway", "this is an obvious custom connector") are the user's call to make, not the agent's. Always ask. SKILL.md Rule 17.
48
52
 
@@ -63,6 +67,77 @@ Each resource type has a `<type>-index.json` file at `~/.uip/case-resources/`:
63
67
 
64
68
  Each file is a JSON array of resource entries.
65
69
 
70
+ ## Create-on-Missing build and rediscovery
71
+
72
+ When the user picks **Create** at the gate, the skill builds each selected resource as an **in-solution sibling** (during Phase-1 planning) and wires it in as a normal resolved task — no placeholder. **v1 builds two kinds — `agent` (via `uipath-agents`) and `api-workflow` (via `uipath-api-workflow`)**; the orchestration below is type-agnostic so other non-connector kinds can be enabled later via their own type skill. Connectors, regular RPA process, and agentic processes (Process Orchestration) are never built here.
73
+
74
+ > **Create depends on the type skill being installed.** The build runs in a sub-agent that invokes the resource's type skill (`uipath-agents` / `uipath-api-workflow`). The Step-2 brief instructs the sub-agent: if it cannot locate/load that skill, return `{built:false, error:"skill <name> not installed"}` — do NOT improvise a build. That `built:false` (or a sub-agent that dies) degrades to a placeholder via the per-plugin Failure contract, and §4 rediscovery is the backstop (no exact-name `--local` match → Failure contract regardless of what the sub-agent reports) — Create never hard-fails the run.
75
+
76
+ ### 0 — Prerequisite (solution must exist) + capability probe (once per run)
77
+
78
+ **Solution prerequisite.** Register (§3) and `--local` rediscovery (§0/§4) all require an enclosing solution `.uipx` (the CLI walks cwd → parent → grandparent — that walk MUST resolve to the case's *own* solution: keep the working root free of an unrelated ancestor `.uipx`, else the built sibling registers into the wrong solution). The Create gate fires in **Phase 1 planning**, *before* [Phase 2 Step 6.0](implementation.md) normally scaffolds the solution — so on a fresh run no `.uipx` exists yet. **When the user selects Create and no `.uipx` is found, run `uip solution init <SolutionName>` first — deriving `<SolutionName>` and its working-root location EXACTLY as Step 6.0 does** (the canonical rule: [plugins/case/planning.md § Naming](plugins/case/planning.md#project-structure-prerequisites)). Do NOT invent a different name/location: Step 6.0 keys its idempotent skip on that exact `.uipx`, so a mismatch double-inits or forks the solution (sibling in one `.uipx`, case project in another). Everything below assumes the `.uipx` now exists.
79
+
80
+ **Capability probe.** Confirm the CLI supports local discovery: run `uip maestro case registry list --local --output json`. Distinguish the failure modes: an **unknown-option** error → `--local` is unsupported → **suppress the Create option entirely** (the gate stays Force pull / Skip), use placeholders. A **`No solution found for --local`** error is NOT a suppress signal — it confirms `--local` IS supported (a missing-solution error, not unknown-option). **Do NOT scaffold at probe time.** At the pre-gate in-solution sibling check (which fires before Create is offered), "No solution found" simply means no sibling exists yet — a solution holds no siblings before it exists — so record "no local sibling" and proceed to the gate. Scaffolding (`uip solution init`, the Solution prerequisite above) happens **only inside the Create flow, after the user selects Create** — never during the probe or the pre-gate sibling check. Offer Create unless the probe returns the unknown-option (unsupported) case. Run the probe **at first need and cache the result for the rest of the run** — whichever comes first: the pre-gate in-solution sibling check ([agent/planning.md](plugins/tasks/agent/planning.md#registry-resolution) / [api-workflow/planning.md § Registry Resolution](plugins/tasks/api-workflow/planning.md#registry-resolution), which also gates on `--local`) or this gate.
81
+
82
+ ### 1 — Select
83
+
84
+ On Create, present an `AskUserQuestion` **multiSelect** of the still-empty **creatable** resources — agents and API workflows. The option list is capped at 4; when >4 creatable resources are empty, batch the selection across successive prompts (≤4 each). Checked → build. Unchecked items **and all non-creatable empties** (regular RPA process, action, connectors, agentic processes, …) → `<UNRESOLVED>` placeholder. If two selected resources share a name — or a selected resource's name is already used by an existing in-solution sibling of another kind (`registry list --local`) — AskUserQuestion to rename before building: the `solution_folder.<name>` sentinel resourceKey and the exact-name `search --local` rediscovery (§4) both key on the name, which must be unique across kinds (the namespace is shared).
85
+
86
+ ### 1b — Choose build kind
87
+
88
+ For each selected **agent**, ask the build kind before building: an `AskUserQuestion` with options **Low-code** and **Coded (Python)** — presented as **equal choices** (neither marked recommended); **one `AskUserQuestion` question per agent** (header = agent name; ≤4 questions per call) — **never one shared question applied to all**. For a non-interactive run the fallback is **Low-code** (`uip agent init`, the platform default kind); coded is `uip codedagent`. The choice sets the brief's `Kind:` line ([agent/planning.md § Creating an Agent inline → Step 2](plugins/tasks/agent/planning.md#creating-an-agent-inline)) and the sub-agent builds that kind. Kind is **never inferred from the SDD**. Coded integration is kind-agnostic on the case side but carries an Orchestrator-deploy caveat — see [agent/planning.md § Coded agents](plugins/tasks/agent/planning.md#creating-an-agent-inline). **API workflows have no kind choice** — skip this step for them; the build is always the JSON-DSL `Workflow.json`.
89
+
90
+ ### 2 — Build (parallel, capped, skip-registration)
91
+
92
+ For each selected resource, compute its build brief — agents per [agent/planning.md § Creating an Agent inline](plugins/tasks/agent/planning.md#creating-an-agent-inline), API workflows per [api-workflow/planning.md § Creating an API workflow inline](plugins/tasks/api-workflow/planning.md#creating-an-api-workflow-inline) — and **spawn one sub-agent per resource that invokes its type skill** (`uipath-agents` / `uipath-api-workflow`). Spawn **up to 10 concurrently; process in waves** if more are selected (cap is a resource throttle, not a safety mechanism). Each sub-agent builds **without registering the project into the solution** (agents: per the §1b kind — low-code `uip agent init --skip-solution-registration` or coded `uip codedagent`; API workflows: `uip api-workflow init <Name> --skip-solution-registration`; the parent registers either way, § 3) and returns `{ built, path, finalInputs[], finalOutputs[], error? }`.
93
+
94
+ The skill itself never runs the type CLI's `init` — build knowledge lives in the type skill. Only gate-selected resources are built; SDD content alone never triggers a build.
95
+
96
+ ### 3 — Register (sequential)
97
+
98
+ The `.uipx` is a shared file; concurrent registration races. So build skips registration, and **the parent registers each built sibling sequentially** after the wave returns:
99
+
100
+ ```bash
101
+ uip solution project add "<built path>" "<solution .uipx>" --output json # one per built sibling, sequential
102
+ ```
103
+
104
+ Both positionals MUST be absolute paths — the relative form fails with `Failed to add project to solution` regardless of CWD (see [implementation.md](implementation.md) § Step 6.0b). Then run `uip solution resources refresh` (Rule 14) so the solution-level resource files + `debug_overwrites.json` are generated before any upload/debug.
105
+
106
+ ### 3b — "Already exists" = adopt (kind-agnostic residual)
107
+
108
+ An interrupted prior run can leave a built sibling **on disk but unregistered**. Nothing that reads `.uipx` `Projects[]` sees it — the pre-gate `--local` check misses, the gate fires, and the build/register step collides: the type's `init` fails *"directory exists / not empty"*, or `uip solution project add` returns *"Project name already exists"*. **Neither is a failure.** Adopt:
109
+
110
+ 1. **Kind-check the collision.** Name present in `uip maestro case registry list --local --output json` → its `Category` identifies a registered owner; a different kind = cross-kind name collision, NOT a prior build → rename the new resource (§1 name-uniqueness) and rebuild. Name absent (`list --local` also reads only `Projects[]`) → read the colliding directory's `project.uiproj` `ProjectType`. Matching kind → adopt:
111
+ 2. **Register.** `uip solution project add` (absolute paths). It can refuse *"Project name already exists"* even when the name is absent from `.uipx` `Projects[]` — its collision check keys on **stale resource declaration files** from a prior registration, not the manifest. Delete `resources/solution_folder/package/<Name>.json` and the kind's `resources/solution_folder/process/<category>/<Name>.json`, re-run `project add`, then `uip solution resources refresh` regenerates them.
112
+ 3. **Continue at §4** (rediscover, verify, bind). Never rebuild, never Retry/Skip, never placeholder — the sibling is already built.
113
+
114
+ Per-type verbs and kind markers: each plugin's § Failure blockquote.
115
+
116
+ ### 4 — Rediscover + verify + bind (offline `--local`)
117
+
118
+ Rediscover **by name** with `search` (not `get`): `registry get <id> --local` matches only on `entityKey`/project Id, never the display name, so `get "<Name>"` returns 0. `search` matches the keyword against the name. Read keys in **PascalCase** (`--output json` PascalCases recursively):
119
+
120
+ ```bash
121
+ uip maestro case registry search "<Name>" --type <agent|api> --local --output json # `agent` for an agent sibling, `api` for an api-workflow sibling
122
+ ```
123
+
124
+ `Code: "ResourceSearchSuccess"`, `Data.ResultCount`, `Data.Resources[]` each `{ ResourceType, Resource: {...} }`. Select the entry whose `Resource.Name` exactly equals `<Name>` and `Resource.Source == "local"` (keyword search may return partial-name matches — filter to the exact name). Use this `--local` result to **confirm the sibling exists** and registered (`Source == "local"`, `Folders[0].FullyQualifiedName == "solution_folder"`). `Resource.EntityKey` is an opaque, derived local key (NOT the `.uipx` `Projects[].Id`, NOT any on-disk id) — **audit-only**; the node binds by name+folder, so never write it. No exact-name local match → the build/registration didn't take → failure contract.
125
+
126
+ > ⚠️ **Do NOT read I/O field names from `Resource.{Inputs,Outputs}`.** `--output json` PascalCases object keys recursively, so the declared property keys come back mis-cased (`poText` → `PoText`, `classification` → `Classification`) — wiring against those names would bind to fields the resource doesn't have. Read the **case-preserving** names + types from the sibling's raw `entry-points.json` on disk instead (`<sibling path>/entry-points.json`), under `entryPoints[0].input.properties` / `.output.properties`. Reading the wrong/absent key returns `undefined`, yielding an empty contract → silent mis-wire. For **api-workflow** siblings apply the fallback chain (flat `input.properties` → `input.schema.document.properties` wrapper → `Workflow.json` root schemas; warn on any fallback — see [api-workflow/planning.md § Registry Resolution](plugins/tasks/api-workflow/planning.md#registry-resolution)). Use `--local` only to locate/confirm the sibling.
127
+
128
+ > Shapes: `search`/`get` nest each entry under `Data.Resources[].Resource.*`; `list --local` flattens to `Data.Resources[].{EntityKey,Name,Category,Source}` (no `Resource` wrapper, no I/O). Use `search` here because it matches by name and confirms the sibling; the sub-agent's returned `finalInputs/finalOutputs` are a liveness signal only — the on-disk `entry-points.json` is authoritative.
129
+
130
+ Then, in order:
131
+
132
+ - **Verify** — reconcile the sibling's declared I/O (case-preserving names from `entry-points.json` per the warning above; agents: CLI-synced; api-workflows: build-kept-consistent — no CLI verb writes entry-point I/O, the builder back-fills it and `validate` won't flag drift) against the pinned contract → matched / missing-in-sibling / extra-in-sibling.
133
+ - **Record** the reconciled contract into `tasks.md` + `registry-resolved.json` and resolve the task (taskTypeId/folder-path filled) — now a normal resolved task. `taskTypeId` holds the local audit-only `EntityKey` as a resolution marker only — **Phase 2 must NOT tenant-`tasks describe` it**; the I/O schema recorded here (from the sibling's `entry-points.json`) is authoritative, and [Phase 2 Step 9 Phase A](implementation.md) + the per-type Built-inline notes ([agent/impl-json.md](plugins/tasks/agent/impl-json.md), [api-workflow/impl-json.md](plugins/tasks/api-workflow/impl-json.md)) skip the gather for inline siblings. (The `caseplan.json` `data.inputs[]`/`data.outputs[]` write and the io-binding pass happen in Phase 2/3 when the resolved task is materialized, exactly like any other resolved resource — **NOT in Phase 1**.)
134
+ - **Warn+diff** missing/extra into the completion report; **never block**. A **missing-in-sibling** pinned output means the Case Variable it feeds is never set — record in the completion report that any downstream reference (task input, condition, SLA) will resolve empty at runtime, so the user can decide to re-invoke the build to add the field or accept it. **Never auto-fabricate** the missing output to silence the warning.
135
+ - **Bind** with `resourceKey="solution_folder.<Name>"` **and `folderPath` binding `default` = `""`** (runtime folder — empty = co-located; the `solution_folder` sentinel lives ONLY in `resourceKey`, NOT in `folderPath`, else runtime `folder not exist` — see [agent/planning.md § Creating an Agent inline](plugins/tasks/agent/planning.md#creating-an-agent-inline) / [api-workflow/planning.md § Creating an API workflow inline](plugins/tasks/api-workflow/planning.md#creating-an-api-workflow-inline)). It binds by name+folder, so `EntityKey` stays audit-only.
136
+
137
+ ### Reject case
138
+
139
+ If a built sibling's task is later dropped (user aborts or removes it on `Request changes`), leave the sibling **on disk** (it is reusable) and **name it in the completion report** ("built but not referenced"). It stays **registered in the `.uipx`** (Step 3 already added it), so it co-deploys with the solution as an unused sibling — harmless; do **not** silently deregister. If the user wants it gone, that is manual cleanup (deregister from the `.uipx` and delete the directory), flagged in the report. Never silently delete it, never silently omit it.
140
+
66
141
  ## Procedure
67
142
 
68
143
  ### 1. Determine Which Cache Files to Search
@@ -118,14 +193,16 @@ for item in data:
118
193
 
119
194
  > **Required precondition.** Before reaching this step, the [§ MUST: Confirm Before Placeholder Fallback](#must-confirm-before-placeholder-fallback) gate above MUST have been satisfied. If you have not yet run AskUserQuestion for the empty-result batch, do that first. Force pull and per-plugin Unresolved Fallback both flow through that gate.
120
195
 
196
+ > **Agents & API workflows: resolve in-solution siblings before counting a lookup empty.** For an `agent` or `api-workflow` that misses the tenant index, first check for an existing in-solution sibling via `registry search "<name>" --type <agent|api> --local --output json` (per [agent/planning.md](plugins/tasks/agent/planning.md#registry-resolution) / [api-workflow/planning.md § Registry Resolution](plugins/tasks/api-workflow/planning.md#registry-resolution)). A `Source: "local"` exact-name match **resolves** it (bind via `solution_folder.<name>`) — it is NOT empty and does NOT reach the gate or Create. Only resources absent from **both** the tenant index and the local siblings are empty here. (This keeps re-runs idempotent: an already-built sibling resolves instead of rebuilding.)
197
+
121
198
  If no match is found across all relevant cache files:
122
199
 
123
- 1. **Already gated above.** AskUserQuestion confirmation already ran. If the user picked `Yes, force pull and re-resolve`, the force pull has already executed; this step is reached for lookups that remained empty after the second-pass search.
200
+ 1. **Already gated above.** AskUserQuestion confirmation already ran. If the user picked `Force pull and re-resolve`, the force pull has already executed; this step is reached for lookups that remained empty after the second-pass search.
124
201
  ```bash
125
- # already executed during the gate's "Yes" branch:
202
+ # already executed during the gate's Force-pull branch:
126
203
  uip maestro case registry pull --force
127
204
  ```
128
- 2. If still no match (or user picked `Skip`), mark it in tasks.md: `[REGISTRY LOOKUP FAILED: <name> in <folder>]` and proceed to the per-plugin Unresolved Fallback path.
205
+ 2. If still no match (or the user picked `Skip and use placeholders`, and any creatable resource was not selected for Create), mark it in tasks.md: `[REGISTRY LOOKUP FAILED: <name> in <folder>]` and proceed to the per-plugin Unresolved Fallback path.
129
206
 
130
207
  ### 4. Return All Matches
131
208
 
@@ -10,7 +10,7 @@ Reason the case shape from the process the user describes — **do not reach for
10
10
 
11
11
  **Stage** — a phase the case works through: a bounded milestone with an *entry* (when it starts), *tasks* (the work done inside it), and a *completion/exit* (when it's done and where the case goes next). Stages are the backbone; they run in sequence (or parallel), wired by **entry/exit conditions** (the case has no edges — transitions are condition-driven). Derive one stage per milestone the user names ("intake", "underwriting", "funding"). Ask: *what is the case working toward right now, and what makes that done?* A stage that "marks the case complete" is on the main flow (`isRequired: true`).
12
12
 
13
- **Secondary stage** (a.k.a. exception stage — `case-management:ExceptionStage`) — work that is **not a fixed step on the line**: it can fire at many points and only under a condition. Errors, escalations, rejections, rework loops, cancellations. Three rules define it, all CLI-enforced:
13
+ **Secondary stage** (a `case-management:Stage` node carrying `data.stageType: "secondary"`the `case-management:ExceptionStage` node type is removed at v22) — work that is **not a fixed step on the line**: it can fire at many points and only under a condition. Errors, escalations, rejections, rework loops, cancellations. Three rules define it, all CLI-enforced:
14
14
 
15
15
  - **No edges** — reached and exited purely by conditions, never wired by an edge. (True of every stage now that edges are retired; the legacy `CASE_MGMT_SECONDARY_STAGE_EDGES` validator that flagged secondary-stage edges is moot.) It is detached from any flow graph.
16
16
  - **Entered by its own condition**, never by an edge — but the entry shape depends on the lane's trigger:
@@ -42,7 +42,7 @@ The case, each stage, and each task move through a lifecycle gated by **rules**
42
42
  How to reason with these:
43
43
 
44
44
  - **`required-*` vs `selected-*`.** `required-tasks-completed` / `required-stages-completed` = "all items flagged required are done" (the `isRequired` flow). `selected-tasks-completed` / `selected-stage-completed` / `selected-stage-exited` = "these *specific named* items." Pairing rule (Key Rule 4): `Marks Complete: Yes` pairs only with `required-*`; `selected-*` is for `No` (routing / early exit / alternate disposition). A `Yes` + `selected-*` pair is a schema error.
45
- - **Secondary (exception) stage** uses **stage-entry + stage-exit rules only, never edges** (true of every stage now — edges are retired). Its entry rule is typically *interrupting* (`isInterrupting: true`); its exit uses `return-to-origin` to rejoin the flow it left. **For a decision/signal-routed lane, the routing lives on the *origin* stage:** a gated diverting exit (`Marks Stage Complete: No`, `IF` on the decision/signal, `exitToStageId` → the lane), with the origin's completion exit gated by the inverse `IF` so the two paths are mutually exclusive — see [§ Logical integrity step 5](#logical-integrity--stage-graph).
45
+ - **Secondary stage** uses **stage-entry + stage-exit rules only, never edges** (true of every stage now — edges are retired). Its entry rule is typically *interrupting* (`isInterrupting: true`); its exit uses `return-to-origin` to rejoin the flow it left. **For a decision/signal-routed lane, the routing lives on the *origin* stage:** a gated diverting exit (`Marks Stage Complete: No`, `IF` on the decision/signal, `exitToStageId` → the lane), with the origin's completion exit gated by the inverse `IF` so the two paths are mutually exclusive — see [§ Logical integrity step 5](#logical-integrity--stage-graph).
46
46
  - **First task in a stage** must carry `current-stage-entered` (emit it explicitly). `wait-for-connector` makes a gate pause for an inbound connector callback — its `conditionExpression` gates on **case state** only (no `event` payload; in-rule extract-then-gate is unsupported at runtime — gate a downstream condition instead); `adhoc` lets a *task* fire manually from the case app (task-entry only — never a stage-entry rule); `runs-sequentially` chains tasks in a lane.
47
47
  - **`user-selected-stage`** (stage entry) starts a stage on demand by a user rather than by flow. The CLI validator requires it to pair with a `wait-for-user` stage exit elsewhere: a `wait-for-user` exit with no `user-selected-stage` entry — or a `user-selected-stage` entry with no `wait-for-user` exit — fails `validate`.
48
48
 
@@ -74,7 +74,7 @@ A task Output *produces* a variable (`-> =vars.<id>`); a task Input *consumes* o
74
74
  Reason the shape; do not template it:
75
75
 
76
76
  - **Milestones → stages:** Intake → Screening → Compliance → Finance Setup → Activation — regular stages on the main flow.
77
- - **"goes back for remediation" → secondary stage.** Remediation fires only on a condition (compliance failed) and routes back — model it as an exception stage (condition-entered, `return-to-origin`), **not** a sixth inline primary stage.
77
+ - **"goes back for remediation" → secondary stage.** Remediation fires only on a condition (compliance failed) and routes back — model it as a secondary stage (condition-entered, `return-to-origin`), **not** a sixth inline primary stage.
78
78
  - **"sign up through portal" → trigger.** A portal signup is an inbound event, not Manual — Always-Ask the trigger type.
79
79
  - **Tasks + types** (read verb + actor, ask the [§ Choosing](#choosing-the-task-type) question):
80
80
  - *screen them* → AI judges unstructured docs → `agent`
@@ -170,7 +170,7 @@ Reason the shape first — [§ Mental model](#mental-model-stages-secondary-stag
170
170
 
171
171
  Phase 1 reads `sdd.md` as written (Rule 2). The following three sections define **what each case / stage / task element MUST contain** before Approve renames the draft. Every block specifies required vs optional cells, allowed values, source of truth, and the fallback when a value is missing.
172
172
 
173
- **Allowed `—`** (cells the user did not touch and Phase 1 can default safely): case-level Description, variable defaults, persona scope notes, app-view detail, exception-stage description, optional `IF` conditionExpressions, business calendars on timers.
173
+ **Allowed `—`** (cells the user did not touch and Phase 1 can default safely): case-level Description, variable defaults, persona scope notes, app-view detail, secondary-stage description, optional `IF` conditionExpressions, business calendars on timers.
174
174
 
175
175
  **Allowed `<UNRESOLVED>`** (gaps Phase 1 / post-build can resolve): registry IDs (`taskTypeId`, `connectionId`, `actionAppId`, `agentId`, `processOrchestrationId`) when Resolve was skipped or returned 0 matches. Pair every `<UNRESOLVED>` with a review item (§Review items).
176
176
 
@@ -227,7 +227,7 @@ Required when Case SLA is set. Always renders with both rows; no `—` allowed i
227
227
 
228
228
  | Trigger type | Write |
229
229
  |---|---|
230
- | Event | The operation in business terms (`Calendar created`, `Email received`). Append a filter clause when the user wants filtering (`Email received in Inbox; filter: subject contains "URGENT"`). Append a required event-param ONLY when the user supplies it (`Email received in folder "<folder name>"`). |
230
+ | Event | The operation in business terms (`Calendar created`, `Email received`, `Record created`). For tenant case-entity / business data-object starts, preserve the object name in Source (`expense_requests`) and write the business event in Configuration (`Record created`). Append a filter clause when the user wants filtering (`Email received in Inbox; filter: subject contains "URGENT"`). Append a required event-param ONLY when the user supplies it (`Email received in folder "<folder name>"`). |
231
231
  | Timer | Cycle or duration (`every 24 hours`, `daily at 09:00 UTC`). |
232
232
  | Manual | `N/A` or omit. |
233
233
 
@@ -240,6 +240,13 @@ Required when Case SLA is set. Always renders with both rows; no `—` allowed i
240
240
 
241
241
  > Variable mapping (which trigger payload field populates which case variable) is declared in **§1.5 Case Variables** via the `sourceTriggers` / `sourceFields` columns — NOT in this table. The Triggers table only identifies and configures each trigger; payload extraction is owned by Case Variables.
242
242
 
243
+ > **Tenant object starts are not Manual.** If the user says a case starts when a
244
+ > tenant case-entity / data-object record is created, record an
245
+ > `Intsvc.EventTrigger` row with the object name as Source. Missing tenant
246
+ > provisioning, absent local registry data, or unresolved connection details
247
+ > become an unresolved event trigger / placeholder later; they are never a reason
248
+ > to change the SDD trigger type to `Manual`.
249
+
243
250
  Unresolved `Intsvc.EventTrigger` resolution (`connectionId` / `activityTypeId` missing) → `high`-severity review item.
244
251
 
245
252
  ### 1.3a Trigger Filter (conditional)
@@ -269,13 +276,13 @@ Nested `{op, clauses}` groups flatten in the rendered table. Avoid `Literal: No`
269
276
 
270
277
  ### 1.4a Case Exit Conditions (alternate disposition)
271
278
 
272
- Optional. `Marks Case Complete: No`. Used for ExceptionStage terminals (Withdrawn / Rejected / Cancelled).
279
+ Optional. `Marks Case Complete: No`. Used for secondary-stage terminals (Withdrawn / Rejected / Cancelled).
273
280
 
274
281
  | WHEN | IF | Marks Case Complete | Exit Type | Display Name |
275
282
  |---|---|---|---|---|
276
283
  | `selected-stage-completed("<Stage Name>")` / `selected-stage-exited("<Stage Name>")` / `wait-for-connector` | optional | `No` | `exit-only` / `wait-for-user` | optional |
277
284
 
278
- **When the case has ≥ 1 ExceptionStage AND Section 1.4a is empty** → emit a `high`-severity review item (`Alt-disposition exits missing`). The case cannot exit non-happy paths cleanly.
285
+ **When the case has ≥ 1 secondary stage AND Section 1.4a is empty** → emit a `high`-severity review item (`Alt-disposition exits missing`). The case cannot exit non-happy paths cleanly.
279
286
 
280
287
  ### 1.5 Case Variables
281
288
 
@@ -287,15 +294,15 @@ A variable used anywhere in the plan that meets the test above appears in this t
287
294
  |---|---|---|
288
295
  | Name | yes | camelCase, no role suffix |
289
296
  | Category | yes | `In` / `Out` / `Variable` — NEVER `—`. Drives the [`global-vars` plugin's](plugins/variables/global-vars/impl-json.md) pattern shape. |
290
- | Type | yes | Platform enum from [case-schema.md § Variables](case-schema.md): `string`, `integer`, `float`, `double`, `boolean`, `datetime`, `date`, `jsonSchema`, `file`. **`file`** is a JobAttachment record (`{ID, FullName, MimeType, Metadata}`) — see `[sdd-template-examples.md](../assets/templates/sdd-template-examples.md)` Use Cases 9–11 for caller-pre-upload, connector-download, and multipart-send patterns. File-typed In-args carry an implicit caller obligation (see §1.5 In semantics + Approve summary reminder). Use `string` for JSON-shaped values; never emit `json` or `jsonSchema`. |
291
- | sourceTriggers | conditional | T-number(s) single `T<N>` or comma-separated CSV (`T02, T03`) when multiple triggers feed the same Variable. Empty for pure state, `In`, `Out`. |
292
- | sourceFields | conditional | Single bare payload path when one trigger; **keyed format** `T<N>: <path>; T<M>: <path>` when `sourceTriggers` is CSV. Dot-paths only — no array indexing in v1. |
297
+ | Type | yes | Platform type enum (see [case-schema.md](case-schema.md)): `string`, `integer`, `float`, `double`, `boolean`, `datetime`, `date`, `jsonSchema`, `file`. **`file`** is a JobAttachment record (`{ID, FullName, MimeType, Metadata}`) — see `[sdd-template-examples.md](../assets/templates/sdd-template-examples.md)` Use Cases 9–11 for caller-pre-upload, connector-download, and multipart-send patterns. File-typed In-args carry an implicit caller obligation (see §1.5 In semantics + Approve summary reminder). Use `string` for JSON-shaped values; never emit `json` or `jsonSchema`. |
298
+ | sourceTriggers | conditional | `Variable`: single `T<N>` or comma-separated CSV (`T02, T03`) when multiple triggers feed the same slot. `In`: optional single `T<N>` selecting the bound trigger (blank = primary trigger T02; never a CSV). Empty for pure state and `Out`. |
299
+ | sourceFields | conditional | `Variable` only: single bare payload path when one trigger; **keyed format** `T<N>: <path>; T<M>: <path>` when `sourceTriggers` is CSV. Empty on `In` rows even when `sourceTriggers` names a trigger (`In` selects a trigger, extracts no field). Dot-paths only — no array indexing in v1. |
293
300
  | Default | optional | Concrete default or empty. |
294
301
  | Description | yes | One-line meaning. |
295
302
 
296
303
  **Category semantics** (canonical definition in [`global-vars/impl-json.md`](plugins/variables/global-vars/impl-json.md)):
297
304
 
298
- - **`In`** — caller-supplied case argument (manual trigger via API) OR `Default`-initialized (event / timer triggers, which have no caller). `sourceTriggers` MUST be empty. For event-payload-extraction, use `Variable` + `sourceTriggers` + `sourceFields` instead (see Use Case 2 in [sdd-template-examples.md](../assets/templates/sdd-template-examples.md)). **`In` of `Type: file`** — programmatic caller must pre-create a JobAttachment (`POST /odata/Attachments` then `PUT` bytes) and pass `{ID, FullName, MimeType, Metadata}` plus `StartProcessDto.Attachments[]`. Maestro Studio Web's "Start case" dialog does this automatically; non-Studio callers do it themselves. Surface this obligation in the Approve summary whenever any file-In-arg exists; see §Finalization step 11.
305
+ - **`In`** — caller-supplied case argument (manual trigger via API) OR `Default`-initialized (event / timer triggers, which have no caller). `sourceTriggers`: blank → binds the primary trigger (T02; default); a single `T<N>` → binds that trigger — never a CSV (one trigger only). `sourceFields` MUST stay empty an In-arg selects a trigger but does not extract a payload field; for payload-extraction use `Variable` + `sourceTriggers` + `sourceFields` (see Use Case 2 in [sdd-template-examples.md](../assets/templates/sdd-template-examples.md)). **`In` of `Type: file`** — programmatic caller must pre-create a JobAttachment (`POST /odata/Attachments` then `PUT` bytes) and pass `{ID, FullName, MimeType, Metadata}` plus `StartProcessDto.Attachments[]`. Maestro Studio Web's "Start case" dialog does this automatically; non-Studio callers do it themselves. Surface this obligation in the Approve summary whenever any file-In-arg exists; see §Finalization step 11.
299
306
  - **`Out`** — case argument returned to caller. Value comes from a producer (a task's Outputs row that targets this Name via `-> {name}` or `{name} = {expr}`) OR from `Default` when no producer fires. `sourceTriggers` MUST be empty (direction mismatch: trigger → case is forbidden for `Out`).
300
307
  - **`Variable`** — case-internal state. Populated by one trigger's payload (`T<N>` + single path), multiple triggers sharing the same slot (CSV + keyed `T<N>: <path>` format), a task's Outputs row, or `Default` only.
301
308
 
@@ -326,12 +333,12 @@ Lineage closure rules in §Variable lineage closure.
326
333
 
327
334
  ## Stage content rules
328
335
 
329
- Defines what each stage in Section 2 must contain. Same rules apply to primary stages (`Stage`) and exception stages (`ExceptionStage`) unless noted.
336
+ Defines what each stage in Section 2 must contain. Same rules apply to primary and secondary stages — both are `Stage` nodes; a secondary stage carries `data.stageType: "secondary"` unless noted.
330
337
 
331
338
  ### Stage heading
332
339
 
333
340
  - Primary: `` ### Stage {N}: {Stage Name} (`{stage_id}`) `` — N is 1-based sequence number
334
- - Exception: `` ### Exception Stage: {Stage Name} (`{stage_id}`) ``
341
+ - Secondary: `` ### Secondary Stage: {Stage Name} (`{stage_id}`) `` — renders a `Stage` node + `Stage Kind: secondary`
335
342
 
336
343
  The trailing `` `{stage_id}` `` (e.g., `` `stage-intake` ``) MUST appear so readers can grep cross-references. Anywhere a stage is referenced by name in a table cell (`Selected Stage`, `Required Stages`, case-exit selected stage), append the stage id in code-formatted parens.
337
344
 
@@ -339,10 +346,11 @@ The trailing `` `{stage_id}` `` (e.g., `` `stage-intake` ``) MUST appear so read
339
346
 
340
347
  | Field | Required? | Value |
341
348
  |---|---|---|
342
- | Type | yes | `Stage` / `ExceptionStage` |
343
- | Description | yes (primary) / optional (exception) | One prose sentence |
344
- | Required for case completion | yes | `Yes` (primary, default) / `No` (ExceptionStages always `No`) |
345
- | Interrupting | ExceptionStage only | `Yes` / `No` does this stage interrupt active stages on activation? |
349
+ | Type | yes | `Stage` |
350
+ | Stage Kind | optional | `primary` (default — omit the line) / `secondary` (emits `data.stageType: "secondary"`; replaces the old `ExceptionStage` type) |
351
+ | Description | yes (primary) / optional (secondary) | One prose sentence |
352
+ | Required for case completion | yes | `Yes` (primary, default) / `No` (secondary stages always `No`) |
353
+ | Interrupting | secondary stages only | `Yes` / `No` — does this stage interrupt active stages on activation? |
346
354
  | Stage SLA | yes when stage has SLA | Duration + type, plus escalation table |
347
355
 
348
356
  ### Stage Entry Conditions table
@@ -357,7 +365,7 @@ The trailing `` `{stage_id}` `` (e.g., `` `stage-intake` ``) MUST appear so read
357
365
 
358
366
  Completion (`Yes`) rows and the §Stage Exit Conditions (`No`) rows below render **together** as the single **Stage Exit Conditions** table in `sdd.md` (per [sdd-template.md](../assets/templates/sdd-template.md)). Shared columns, in order: `WHEN | IF | Exit Type | Marks Stage Complete | Display Name`. ≥ 1 completion row required. **Regular stage-to-stage routing is NOT carried here** — each destination stage declares the link via its own Entry Condition (`selected-stage-completed("This Stage")` / `selected-stage-exited("This Stage")`), so one stage can fan out to N stages.
359
367
 
360
- > **Carve-out — routing INTO a decision/signal-routed exception lane IS carried here.** This is the one case where the origin stage carries the route: add a **gated diverting exit row** (`Marks Stage Complete: No`, WHEN `selected-tasks-completed("<decider task>")`, `IF` on the decision/signal, `Exit Type: exit-only`, `exitToStageId` → the exception stage) AND gate this stage's completion row with the **inverse `IF`** so completion and divert are mutually exclusive. Without the diverting exit, the decision path either dual-fires (ungated completion → both the next stage and the lane enter) or deadlocks (gated completion with no alternative exit). See [§ Logical integrity step 5](#logical-integrity--stage-graph) and the worked example below.
368
+ > **Carve-out — routing INTO a decision/signal-routed exception lane IS carried here.** This is the one case where the origin stage carries the route: add a **gated diverting exit row** (`Marks Stage Complete: No`, WHEN `selected-tasks-completed("<decider task>")`, `IF` on the decision/signal, `Exit Type: exit-only`, `exitToStageId` → the secondary stage) AND gate this stage's completion row with the **inverse `IF`** so completion and divert are mutually exclusive. Without the diverting exit, the decision path either dual-fires (ungated completion → both the next stage and the lane enter) or deadlocks (gated completion with no alternative exit). See [§ Logical integrity step 5](#logical-integrity--stage-graph) and the worked example below.
361
369
 
362
370
  | WHEN | IF | Exit Type | Marks Stage Complete | Display Name |
363
371
  |---|---|---|---|---|
@@ -691,7 +699,7 @@ Pattern X1 is preferred unless an actual connector emits the close event. When t
691
699
 
692
700
  1. Every variable referenced by any `=vars.<name>` (or `=vars.<name>.<sub>`) anywhere in `sdd.md` (task Inputs, IF columns, exit rules, button `Maps To`, SLA expressions) has a matching §1.5 row whose `Name` equals `<name>` — OR `<name>` is an upstream task's auto-emitted output field (see §Variable lineage closure → Task-output direct reference; never add a §1.5 row to back such a ref).
693
701
  2. Every §1.5 row's `Category` is exactly one of `In` / `Out` / `Variable` — never blank, never `—`.
694
- 3. **`In` row consistency:** `sourceTriggers` and `sourceFields` are BOTH empty.
702
+ 3. **`In` row consistency:** `sourceFields` is empty. `sourceTriggers` is empty (binds the primary trigger) OR a single `T<N>` that exists in tasks.md (binds that trigger) — never a CSV. A CSV `sourceTriggers`, or any non-empty `sourceFields`, on an `In` row is an error.
695
703
  4. **`Out` row consistency:** `sourceTriggers` is empty. Closure requires either (a) non-empty `Default`, OR (b) a task Outputs row in the case plan targeting this Name via `-> {name}` or `{name} = {expr}`. (PR 860 added a Phase 2 validator: `Out` + non-empty `sourceTriggers` → reject.)
696
704
  5. **`Variable` row consistency:** if `sourceTriggers` is non-empty, `sourceFields` MUST have a matching entry for every T-number listed. For CSV `sourceTriggers`, `sourceFields` MUST use keyed `T<N>: <path>; T<M>: <path>` format with one keyed entry per T-number — strict, no defaults. Single-T-number rows use a bare path.
697
705
  6. **Stage-order closure.** For each consumer of `vars.<caseVar>`, identify producers (trigger-extraction, task Outputs row `->` or `=`). At least one producer's stage index ≤ consumer's stage index AND (same stage) task index < consumer's task index. If no producer exists, the §1.5 row MUST satisfy the `Category: In` or non-empty `Default` escape.
@@ -723,7 +731,7 @@ Severity:
723
731
  |---|---|---|
724
732
  | **high** | Blocks Phase 1 / `caseplan.json` build until resolved. | Missing `connectionId` for a resolved connector task; missing `actionAppId` for an `action` task; missing deployed `process` / `agent` / `api-workflow` for a runnable task; a resolved resource's **required** input left unbound (`rev_unbound_input_<task>_<field>`); an extract output naming a field the resource does not emit (`rev_phantom_output_<task>_<field>`); unresolved variable lineage; missing trigger config; compliance-override conflict the user has not reconciled. |
725
733
  | **medium** | Phase 1 can default with a prompt. | Missing SLA escalation recipient (default = owner group); missing variable default; ambiguous recipient (persona name without group resolution). |
726
- | **low** | Cosmetic. | Missing case-level description; missing exception-stage description; stylistic placeholder. |
734
+ | **low** | Cosmetic. | Missing case-level description; missing secondary-stage description; stylistic placeholder. |
727
735
 
728
736
  **Approve gate behavior.** When any `high` review items exist, Approve adds an explicit follow-up: `Approve despite N high-severity items` (with the count populated). User must opt in — silent approval is forbidden. Medium and low items show in the Approve summary count but do not require explicit acknowledgment.
729
737
 
@@ -754,10 +762,10 @@ Phase 0's narrative cells (Description, persona names, stage names, task names,
754
762
  Beyond schema-pairing checks (§Finalization step 1), the case must be a connected graph. **Edges are retired — these condition-based checks are the SOLE reachability guard; there is no edge graph to fall back on.** A malformed or missing entry condition is the only thing that can orphan a stage, so this walk is load-bearing.
755
763
 
756
764
  1. **Every stage reachable from a trigger.** Walk forward from each trigger row through Stage Entry Conditions (`case-entered` from root, `selected-stage-completed`, `selected-stage-exited`, `wait-for-connector`) — condition-only, no edges. Every primary stage's id must be reached. Unreachable stage → blocking error (orphan stage).
757
- 2. **Every stage exits.** Every primary stage must have either (a) a completion row (`Marks Stage Complete: Yes`) whose completion is consumed by a downstream stage's Entry Condition or a case-exit, OR (b) another primary stage whose Entry Condition references it (`selected-stage-completed`/`selected-stage-exited`), OR (c) feed an ExceptionStage. A stage no other stage (or case-exit) keys off → blocking error (terminal-loop stage).
765
+ 2. **Every stage exits.** Every primary stage must have either (a) a completion row (`Marks Stage Complete: Yes`) whose completion is consumed by a downstream stage's Entry Condition or a case-exit, OR (b) another primary stage whose Entry Condition references it (`selected-stage-completed`/`selected-stage-exited`), OR (c) feed a secondary stage. A stage no other stage (or case-exit) keys off → blocking error (terminal-loop stage).
758
766
  3. **Every case-exit row references a stage that exists.** No dangling `Required Stages` references.
759
767
  4. **Every `Required Stages` cell in §1.4 names ≥ 1 primary stage with `Required for case completion: Yes`.** Otherwise the case can never complete.
760
- 5. **ExceptionStages must have ≥1 entry condition, each DISTINCT, chosen by trigger source.** Map the lane's *trigger* to the rule: a gate decision → `selected-stage-completed` / `selected-stage-exited` (+ `IF` on the decision var); a person launches it → `user-selected-stage`; an external event → `wait-for-connector`. `adhoc` is task-entry only — never a stage entry. Two ExceptionStages whose entry rules are identical (same rule type + `selectedStageId` + `conditionExpression`) fail `validate` (`CASE_MGMT_SECONDARY_STAGE_ENTRY_RULES_DUPLICATE`) — give each a distinct `selectedStageId` or `conditionExpression` guard. Set `Interrupting: Yes` for lanes that fire mid-stage (escalation, comms, withdrawal). Terminal lanes (Rejected / Withdrawn) exit `exit-only` and declare a §1.4a case-exit (`marks-case-complete: false`); return lanes (Escalation / Customer Comms) exit `return-to-origin`. **Decision-reachable lanes:** when any decision button's Behavior (or the user's stated intent) names an ExceptionStage as a destination ("route to / send to / escalate via the X lane"), that lane's entry conditions MUST include a `selected-stage-completed` / `selected-stage-exited` rule with an `IF` on the deciding variable's value. A lane described as decision-reachable but entered ONLY via `wait-for-connector` (no decision-keyed entry) is unreachable from its stated source → blocking error. A `wait-for-connector` entry may coexist as a separate trigger, but cannot be the lane's only entry when a decision is supposed to reach it. **A `selected-stage-completed`/`selected-stage-exited` lane entry REQUIRES a matching origin diverting exit — the entry alone is not enough.** On the *origin* stage add a **gated diverting exit** (`Marks Stage Complete: No`, WHEN `selected-tasks-completed("<decider task>")`, `IF =js:(<signal> === <exception-value>)`, `exit-only`, `exitToStageId` → the lane) **and** gate the origin's completion exit with the inverse `IF` (`=js:(<signal> !== <exception-value>)`) so the two are mutually exclusive. Without the diverting exit the decision path either **dual-fires** (ungated completion → the next stage *and* the lane both enter) or **deadlocks** (gated completion with no alternative exit). `selected-stage-exited` fires *after* the origin exits, so this is a **divert-and-return, not a true mid-stage interrupt** — a genuine mid-stage interrupt needs `user-selected-stage` or `wait-for-connector` (mental-model shape (a)). Missing origin diverting exit, or a completion exit not mutually exclusive with it → blocking error.
768
+ 5. **Secondary stages must have ≥1 entry condition, each DISTINCT, chosen by trigger source.** Map the lane's *trigger* to the rule: a gate decision → `selected-stage-completed` / `selected-stage-exited` (+ `IF` on the decision var); a person launches it → `user-selected-stage`; an external event → `wait-for-connector`. `adhoc` is task-entry only — never a stage entry. Two secondary stages whose entry rules are identical (same rule type + `selectedStageId` + `conditionExpression`) fail `validate` (`CASE_MGMT_SECONDARY_STAGE_ENTRY_RULES_DUPLICATE`) — give each a distinct `selectedStageId` or `conditionExpression` guard. Set `Interrupting: Yes` for lanes that fire mid-stage (escalation, comms, withdrawal). Terminal lanes (Rejected / Withdrawn) exit `exit-only` and declare a §1.4a case-exit (`marks-case-complete: false`); return lanes (Escalation / Customer Comms) exit `return-to-origin`. **Decision-reachable lanes:** when any decision button's Behavior (or the user's stated intent) names a secondary stage as a destination ("route to / send to / escalate via the X lane"), that lane's entry conditions MUST include a `selected-stage-completed` / `selected-stage-exited` rule with an `IF` on the deciding variable's value. A lane described as decision-reachable but entered ONLY via `wait-for-connector` (no decision-keyed entry) is unreachable from its stated source → blocking error. A `wait-for-connector` entry may coexist as a separate trigger, but cannot be the lane's only entry when a decision is supposed to reach it. **A `selected-stage-completed`/`selected-stage-exited` lane entry REQUIRES a matching origin diverting exit — the entry alone is not enough.** On the *origin* stage add a **gated diverting exit** (`Marks Stage Complete: No`, WHEN `selected-tasks-completed("<decider task>")`, `IF =js:(<signal> === <exception-value>)`, `exit-only`, `exitToStageId` → the lane) **and** gate the origin's completion exit with the inverse `IF` (`=js:(<signal> !== <exception-value>)`) so the two are mutually exclusive. Without the diverting exit the decision path either **dual-fires** (ungated completion → the next stage *and* the lane both enter) or **deadlocks** (gated completion with no alternative exit). `selected-stage-exited` fires *after* the origin exits, so this is a **divert-and-return, not a true mid-stage interrupt** — a genuine mid-stage interrupt needs `user-selected-stage` or `wait-for-connector` (mental-model shape (a)). Missing origin diverting exit, or a completion exit not mutually exclusive with it → blocking error.
761
769
  6. **Classify each secondary stage's `Interrupting` flag by whether it must halt active work.** `Interrupting: Yes` pauses the active stage(s) when the lane fires; `Interrupting: No` runs alongside them in parallel while the main flow continues. Choose the value from the lane's intent — does handling it require stopping the rest of the case, or can it proceed concurrently? Interrupting is independent of whether the lane is terminal or returning. The one hard rule: a `return-to-origin` exit **requires `Interrupting: Yes`** — the case can only return to a stage it interrupted, so a non-interrupting `return-to-origin` lane is incoherent → blocking error.
762
770
 
763
771
  **Worked example — decision/signal-routed return exception (AP Review → SLA Escalation).** The origin "AP Review" routes to the exception lane "SLA Escalation" on a `requiresEscalation` decision, then returns:
@@ -786,7 +794,7 @@ Phase 0's job is to surface execution-readiness gaps, not just schema validity.
786
794
  | **All-`action` stage** | A stage's tasks are 100% `action` AND stage has > 2 tasks | `rev_human_only_<stage>`: "All tasks in this stage are HITL — consider whether agent / process / api-workflow can pre-fill or pre-screen before human review." |
787
795
  | **Missing happy-path exit on first stage** | The first primary stage has only routing exits (`Marks Stage Complete: No`) and no `required-tasks-completed` row | `rev_no_happy_path_<stage>`: "First stage has no happy-path completion — the case may not reach Stage 2 cleanly." |
788
796
  | **Decision-button outcome unread** | An `action` task with `is_decision: Yes` writes a case variable in its `Maps To` cell AND that variable is NOT consumed by any downstream condition / stage entry / task input / case exit | `rev_orphan_decision_<task>`: "Decision button writes `<var>` but no downstream rule reads it — branching has no effect on case path. Either consume the variable or downgrade `is_decision` to No." |
789
- | **Connector-task failure has no exception path** | `execute-connector-activity` / `wait-for-connector` task in a primary stage AND no ExceptionStage entered via `wait-for-connector` failure or task failure rule | `rev_no_failure_path_<task>` (`medium`; **`high`** when ≥ 2 connector tasks share a primary critical path with zero exception cover): "Connector activity in critical path with no exception-stage cover — runtime failure halts the case." |
797
+ | **Connector-task failure has no exception path** | `execute-connector-activity` / `wait-for-connector` task in a primary stage AND no secondary stage entered via `wait-for-connector` failure or task failure rule | `rev_no_failure_path_<task>` (`medium`; **`high`** when ≥ 2 connector tasks share a primary critical path with zero exception cover): "Connector activity in critical path with no secondary-stage cover — runtime failure halts the case." |
790
798
  | **Generic action app reused as a substitute** | ONE resolved Action App ID bound to ≥ 2 `action` tasks **that do NOT each carry a distinct `actionType`**, OR where a declared field is absent from the app's schema (won't bind). **Exempt:** a code-switched app — distinct `actionType` per task **and** every declared field ⊆ the app schema — is the sanctioned normalized-action-app pattern, NOT flagged. | `rev_substitute_app_<app>` (`high`): "Action app reused across N tasks without a code-switching `actionType` (or with fields the app does not expose) — declared fields will not bind. Make it a code-switched app (distinct `actionType` per task, fields ⊆ the app schema) or deploy task-specific apps." |
791
799
  | **Multiple parallel single-recipient bottlenecks** | ≥ 2 stages have single-recipient bottleneck check fire AND they fan-in to the same downstream stage | `rev_multi_bottleneck_<stages>`: "Multiple single-recipient bottlenecks gate a downstream stage — fan-in stalls cascade." |
792
800
  | **Case-var relay (over-declaration)** | A §1.5 `Variable` row whose **only** producer is one task's Outputs `->` row AND whose **only** consumer is one downstream binding (one task Input `=vars.X`, OR one `=js:` expression / `IF`) — i.e., it carries a single output to a single consumer and is neither `In`/`Out` nor read in ≥ 2 places. **Exempt:** rows that rename, set a custom `Default` / `Type` / `Description`, or are read by ≥ 2 consumers / a condition. | `rev_relay_var_<name>`: "Variable `<name>` relays one task's output to a single consumer — reference the output directly (`<- \"Stage\".\"Task\".out` or `vars.$xref('Stage','Task','out')`) and drop the §1.5 row (see § 1.5 declare-vs-xref test)." |
@@ -828,7 +836,7 @@ Before Approve atomic-renames `sdd.draft.md` → `sdd.md`, Phase 0 runs these ch
828
836
  5. **Connector-id check.** Every `wait-for-connector` / `execute-connector-activity` **task** has concrete `Connection ID` AND `Activity Type ID`. Every `wait-for-connector` **condition rule** (in any scope — stage-entry / stage-exit / case-exit / task-entry) has a `Connector Rule Detail` block resolving to a concrete `Connector Key` AND `Event Operation` (and `Connection ID` when not a tenant-default). Missing identity → paired `high`-severity review item.
829
837
  6. **Variable-lineage check.** Every variable closes (producer before consumer; no orphans).
830
838
  7. **Override-conflict check.** No compliance trigger phrase paired with a non-`action` task type without explicit user reconciliation in the transcript.
831
- 8. **Alt-disposition coverage.** If ≥ 1 ExceptionStage exists, Section 1.4a is non-empty OR a `high`-severity review item is open.
839
+ 8. **Alt-disposition coverage.** If ≥ 1 secondary stage exists, Section 1.4a is non-empty OR a `high`-severity review item is open.
832
840
  9. **Review-items high-severity acknowledgment.** Approve adds the explicit follow-up when `high` items exist.
833
841
  10. **Source-ledger check.** Every non-`user-stated` and non-`verbatim` field has provenance.
834
842
  11. **File-In-arg caller-obligation surfacing.** When ≥ 1 §1.5 row has `Category: In` AND `Type: file`, the Approve summary MUST include a `Caller obligation` block:
@@ -844,7 +852,7 @@ Before Approve atomic-renames `sdd.draft.md` → `sdd.md`, Phase 0 runs these ch
844
852
 
845
853
  This is informational, not blocking. But missing it suppresses a known integration gotcha.
846
854
 
847
- 12. **Stage-graph connectivity check.** Run the §Logical integrity stage-graph checks (every stage reachable, every stage exits, every Required Stages cell points to existing primary stages, every ExceptionStage has ≥ 1 entry condition, and each ExceptionStage's Interrupting flag matches its exit — `return-to-origin` ⟹ `Interrupting: Yes`). Any failure → blocking error.
855
+ 12. **Stage-graph connectivity check.** Run the §Logical integrity stage-graph checks (every stage reachable, every stage exits, every Required Stages cell points to existing primary stages, every secondary stage has ≥ 1 entry condition, and each secondary stage's Interrupting flag matches its exit — `return-to-origin` ⟹ `Interrupting: Yes`). Any failure → blocking error.
848
856
  13. **Domain-fidelity scan.** Run a single pass over every narrative cell (Description, persona name, stage name, task name, button label, app-view purpose). For each customer-named entity surfaced in §Source ledger as `verbatim:"..."`, confirm the rendered cell still uses the verbatim phrase (no synonym drift). Mismatch → list and offer `Re-edit` with the verbatim phrase pre-filled.
849
857
  14. **Architect's-lens advisory pass.** Run the §Architect's lens checks. Emit `medium` review items for each trigger (the `high` variants — `rev_substitute_app`, and `rev_no_failure_path` at the ≥ 2-connector threshold — emit `high` and gate via the opt-in). `medium` is non-blocking; Approve summary surfaces the count.
850
858
  15. **Decision-routing closure.** For every `action` task with `is_decision: Yes`, each button's `Maps To` variable+value MUST be consumed by ≥ 1 downstream rule (stage-entry `IF`, task-entry `IF`, stage-exit, or case-exit) OR the button's Behavior MUST declare it terminal (no routing claim). When a button's Behavior names a destination stage / lane ("route to / send to / via the X lane") and no entry condition keys off that variable+value, the branch is dead → **blocking error**. Pair with §Logical integrity step 5 (lane reachability). A fully-orphaned decision variable (produced by a button, read by nothing) on an `is_decision: Yes` task is blocking; the `medium` `rev_orphan_decision` variant in §Architect's lens applies only when the variable IS read but not for branching.
@@ -869,7 +877,7 @@ On fail: list the specific failing checks, return to AskUserQuestion `Re-edit` /
869
877
  - **Do NOT emit `external-agent`, `external-workflow`, `document-extraction`, `flow-process`, `connector-activity`, `connector-trigger`, or `wait-for-event` as task types.** This skill generates 9 of the CLI's 10 types (Rule 16). `external-agent`, `external-workflow`, `document-extraction`, and `flow-process` are **not supported yet**. The rest are not CLI task types at all.
870
878
  - **Do NOT author task inputs as bare field-name lists** (`**Inputs:** a, b, c`). Use the `Field | Type | Binding` table — bare lists force Phase 1 into name-match inference.
871
879
  - **Do NOT close variable lineage by guessing producers.** If no producer fires before a consumer AND the §1.5 row has no `Default`, that is an open-lineage error — surface it. Never silently retag the row's `Category` to `In` or invent a `Default` to suppress the failure.
872
- - **Do NOT populate `sourceTriggers` on `In` or `Out` rows.** PR 860 added a Phase 2 validator that rejects `Out` + non-empty `sourceTriggers`. For trigger-payload extraction, use `Category: Variable` (see §1.5 and [sdd-template-examples.md](../assets/templates/sdd-template-examples.md) Use Case 2).
880
+ - **Do NOT populate `sourceTriggers` on `Out` rows.** PR 860's Phase 2 validator rejects `Out` + non-empty `sourceTriggers` (direction mismatch). An `In` row MAY carry a single `T<N>` to bind to a specific trigger (blank = primary), but its `sourceFields` MUST stay empty and a CSV is forbidden. For trigger-payload extraction, use `Category: Variable` (see §1.5 and [sdd-template-examples.md](../assets/templates/sdd-template-examples.md) Use Case 2).
873
881
  - **Do NOT use bare `sourceFields` paths when `sourceTriggers` is CSV.** Multi-trigger rows MUST use keyed `T<N>: <path>; T<M>: <path>` format with one entry per T-number. Mismatch is a Phase 2 validator error.
874
882
  - **Do NOT mix `->` and `=` operators on the same target case variable within one task's Outputs.** Each target appears in at most one row per task — no double-binding.
875
883
  - **Do NOT leak skill-internal vocabulary into SDD narrative cells.** `Pattern C`, `bridge`, `companion`, `io-binding`, `dispatcher`, `Finding #N`, `aliased into`, `auto-mint`, etc. belong inside skill references — not in `sdd.md` Descriptions or notes. See [sdd-template.md § Output Rules](../assets/templates/sdd-template.md).