Footprint Workflows
Author or modify a Datex Studio footprint-workflow (configurationTypeId: 23) on a branch — a low-code TypeScript implementation that the Footprint platform invokes at a named extension point in its own processing (before an entity status commits, while cartonizing, while planning allocation, while recommending a location, …). It is the modern replacement for the legacy XAML "Datex Workflow" activities. The platform owns the slot, the GUID, and the input/output contract; you own the body.
References
- ../datex-studio-shared/branch-setup.md — Branch/connection selection (shared across skills)
- references/footprint-workflows.md — Authoritative reference: body shape, the slot binding + workflowsMetadata discovery, the fixed param contract, per-slot field breakdowns, the extension-point catalog, code patterns, CLI lifecycle, pre-flight checklist
- ../datex-studio-shared/configuration-roundtrip.md — The
get -O envelope → jq .json → upsert -D round-trip and the silent-wipe bug it avoids
- ../datex-studio-conventions/file-format.md —
configurationTypeId table, code-string editing rules, the return;-with-outParams rule
- ../datex-studio-conventions/naming-conventions.md —
_workflow suffix preference; backend types are not bound by the display-name rule
- ../datex-studio-conventions/universal-checklist.md — cross-cutting checks every component must pass
- ../datex-studio-runtime/calling-conventions.md — execution-tier rules; workflow runs action-tier (calls actions via
$flows, reads fpds, no $db, no functions)
- ../datex-studio-shared/flow-code-patterns.md —
$utils.isDefined, date defaulting, and other flow-code idioms
- ../action-creator/references/actions.md — actions are the dispatch target the workflow body calls; same Footprint-server tier
- ../datasource-creator/references/datasources.md —
fpds_* footprint-datasources the workflow reads (action-tier)
Dependencies
requirements-gathering skill — invoked to produce a requirements brief if one doesn't already exist in context (which slot, what the custom behavior is, what it dispatches to, which package).
action-creator skill — invoked to author the package actions the workflow body dispatches to. Keep the workflow node a thin dispatcher; real logic lives in actions (same Footprint-server tier).
function-creator skill — invoked when the dispatched logic is better expressed as a function wrapped behind an action (workflows can't call functions directly).
datasource-creator skill — invoked when the workflow reads configuration/state through a fpds_* footprint-datasource.
type-definition-creator skill — invoked when the dispatch targets need package interfaces/enums (the FootPrintWorkflow.* input/result types are platform-owned and read from contexts, not authored).
impact-analysis skill — invoked before changing a workflow that other code depends on, or before renaming the actions/datasources it dispatches to; trace $flows.<Package>.<action> call sites rather than grepping inline.
CLI Lifecycle
Workflow authoring goes through dxs configuration — the generic CRUD primitive over every platform configuration type. There is no dxs workflow subcommand and no field-level patching; you build (or fetch + extract) the whole JSON body, edit it, and push the whole thing back. The CLI type identifier is footprintworkflow (lowercase, matches ConfigurationEndpoints.normalize_type output), mapping to configurationTypeId: 23. PublishedMain configs are readonly: true — author on a feature branch.
New workflow — discover the slot from the metadata API, assign a fresh GUID (replacing a legacy
workflow instead? reuse its GUID — see step 2):
# 1. Discover the slot: id (= workflowDefinitionId), name, exact inParams/outParams
dxs api GET "/footPrintApiConnections/byName/<connectionName>/workflowsMetadata?applicationId=<branchId>" --raw -O meta.json
jq '.workflowsMetadataJson.workflowDefinitions[] | select(.name=="Cartonization")' meta.json
# 2. Generate a fresh workflowGUID for this new config (new capability; you point callers at it).
# Superseding a legacy workflow on this slot? Reuse ITS GUID instead so existing callers keep working.
python3 -c "import uuid; print(uuid.uuid4())"
# 3. Build body.json: apiSettingName + workflowDefinitionId/Name (step 1) + your fresh GUID,
# the slot's inParams/outParams verbatim, configurationTypeId:23, id:0, your referenceName/title/description/code
# 4. (Optional) read Input/result field shapes — contexts OR meta.json `types`
dxs configuration contexts footprintworkflow -b <branchId> -D body.json
# 5. Validate, THEN upsert — these are two steps, not one. Validate exits 1 when it finds
# errors (read validation_errors, fix body.json, re-run); do not run the upsert on exit 1.
dxs configuration validate footprintworkflow -b <branchId> -D body.json
dxs configuration upsert footprintworkflow -b <branchId> -D body.json
Edit an existing workflow (round-trip — never skip the jq extract):
dxs configuration get footprintworkflow <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json # EXTRACT THE INNER BODY (round-trip footgun guard)
# ... edit nodes[0].stepConfig.executeCodeConfig.code ...
dxs configuration validate footprintworkflow -b <branchId> -D body.json # exit 1 = errors found; fix, do not push
dxs configuration upsert footprintworkflow -b <branchId> -D body.json
Round-trip rule (critical)
Never pipe envelope.json directly into dxs configuration upsert — it silently destroys config content (the envelope carries id/jsonString/version/… that upsert -D doesn't expect). Always jq .json envelope.json > body.json first. The envelope also carries Azure app-registration secrets in application.applicationDefinition — the jq .json extract drops them; never commit envelope.json. See ../datex-studio-shared/configuration-roundtrip.md.
Workflow
[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection (feature branch — main is readonly)
|
[requirements brief in context?]
YES -> use it NO -> invoke `requirements-gathering`
|
[Phase 2: Identify the platform slot]
Pick the extension point (Cartonization, Entity Status Change (Before Commit),
Allocation Strategy, Recommend* , Barcode Parser, ...). Consult
references/footprint-workflows.md -> Extension-Point Catalog.
If nothing in the platform *calls* the slot -> a workflow config is inert; stop.
|
[Phase 3: Bind to the slot]
Pull the workflowsMetadata API for the connection/branch -> copy the slot's
id (=workflowDefinitionId), name, and inParams/outParams VERBATIM. Never
invent the id/name. workflowGUID is NOT in the metadata — it is the code callers
pass: generate fresh for a NEW workflow, preserve it on edits, and reuse the
legacy GUID when shipping a drop-in replacement for an existing workflow.
|
[Phase 4: Author the body]
Build body.json:
- Slot binding (Phase 3): apiSettingName + workflowDefinitionId/Name +
your workflowGUID + configurationTypeId:23 + start:"step1"
- Fixed param contract: single Input: FootPrintWorkflow.<Slot>InputBaseWL;
out-params exactly as the slot dictates (or [] for before-commit mutation)
- Single ExecuteCodeActivity node; code is a THIN DISPATCHER to package
actions ($flows.<Pkg>.<action>) and fpds reads; map results into
$flow.outParams; action-tier rules (no $db, no functions)
- id:0, accessModifier:public, description <=100 chars, vars/events null
|
[Phase 5: Discover types + validate]
dxs configuration contexts footprintworkflow -b <branchId> -D body.json
-> confirm $types.FootPrintWorkflow.<X>InputBaseWL + result fields you use
dxs configuration validate footprintworkflow -b <branchId> -D body.json
|
[Phase 6: Push]
CREATE -> dxs configuration upsert footprintworkflow -b <branchId> -D body.json
MODIFY -> get -O envelope -> jq .json -> edit -> upsert (round-trip)
|
[Phase 7: Verify (optional)]
Exercise the slot in the running platform; confirm the workflow is invoked and
the result/mutation lands. Re-fetch (jq .json) and diff against body.json.
|
[invoke `post-edit-verification`; then `component-validator`]
Phase Details
Phase 1: Setup + Requirements
- Follow ../datex-studio-shared/branch-setup.md. Never assume a branch ID — confirm with the user. Author on a feature branch; PublishedMain workflow configs are
readonly: true.
- Check for a requirements brief in context. The brief should establish: which platform slot, the custom behavior, what package actions/datasources it dispatches to, and the target package. No brief → invoke
requirements-gathering.
Phase 2: Identify the platform slot
A footprint-workflow only runs if the Footprint platform calls its slot. Consult references/footprint-workflows.md → Purpose & When to Use and the Extension-Point Catalog. If the desired behavior isn't triggered by one of the named workflow slots, this is the wrong component type — a plain action/function/datasource is what's needed. If the signature should be yours to design, it's a function/action, not a workflow.
Phase 3: Bind to the slot
The slot binding has two independent parts:
- Slot identity + signature (from the platform). Pull the workflowsMetadata API for the Footprint connection and branch:
dxs api GET "/footPrintApiConnections/byName/<connectionName>/workflowsMetadata?applicationId=<branchId>" --raw -O meta.json
jq '.workflowsMetadataJson.workflowDefinitions[] | {id, name, inParams, outParams}' meta.json
Copy the slot's id → workflowDefinitionId, name → workflowDefinitionName, and its inParams/outParams verbatim. <connectionName> is a real Footprint connection (e.g. DSV), not the apiSettingName value. Set apiSettingName from the branch's Footprint setting. Never invent the id/name.
workflowGUID (the code callers pass). It is not in the metadata, so nothing in the catalog dictates it — but callers do: the value reaches the server as ProcessingStrategyWorkflowCode / AllocationStrategyWorkflowId and selects which implementation of the slot runs. Generate a fresh v4 UUID for a genuinely new workflow (python3 -c "import uuid; print(uuid.uuid4())") and then point the callers at it; preserve the existing GUID unchanged when editing (the jq .json round-trip keeps it); reuse the legacy GUID when your config is a drop-in replacement for an existing implementation whose callers must not change. Never regenerate on an edit.
Phase 4: Author the body
Build body.json from references/footprint-workflows.md → Minimal Valid Skeleton. Key points:
- File basics per the Pre-Flight Checklist below + universal checklist:
configurationTypeId: 23, id: 0, description ≤ 100 chars, accessModifier set, package matches the feature.
- Param contract is the platform's, not yours. Single
Input in-param typed FootPrintWorkflow.<Slot>InputBaseWL; out-params exactly as the slot dictates (one object, a collection, or [] for before-commit mutation). Don't rename Input, add params, or change the out-param shape.
- Single
ExecuteCodeActivity node. start: "step1", node id: "step1", decisionConfig: null. Multi-node graphs are possible but unused in the library — single step unless you have a concrete reason.
- Code is a thin dispatcher. Translate
$flow.inParams.Input into $flows.<Package>.<action> calls and fpds reads; map results onto $flow.outParams.<Name>. Action-tier rules: no $db, no direct function calls, fpds (not cloud -datasource.json). Declared-outParams slots use return $flow.outParams;, never bare return;. See references/footprint-workflows.md → Code Patterns.
vars / events / fromBaseConfiguration stay null.
Phase 5: Discover types + validate
dxs configuration contexts footprintworkflow -b <branchId> -D body.json
dxs configuration validate footprintworkflow -b <branchId> -D body.json
contexts returns the $types.FootPrintWorkflow.* IntelliSense surface, and its flowContext echoes your declared signature as a typed IFlow — a quick check that $flow.inParams.Input / $flow.outParams.* resolve to the slot's types. Confirm every field you reference exists (don't hand-roll the platform types). Three discovery sources, by need (see references → Discovering the type surface):
FootPrintWorkflow.* platform types (the Input/result shapes) — contexts, or the workflowsMetadata API's types array (structured JSON, easier to query).
$types.<Package>.* package types your dispatch code uses (e.g. $types.Utilities.e_awi_scopes.Cartonization) — dxs configuration nomenclature -b <branchId> (filter with --package/--kind enum/--search; enums list members in constantValues).
validate catches structural/required-field errors but not a wrong slot binding (workflowDefinitionId/name), a signature that doesn't match the slot, or action-tier violations — walk the Pre-Flight Checklist for those.
Phase 6: Push
# New
dxs configuration upsert footprintworkflow -b <branchId> -D body.json
# Modify-existing (round-trip — never skip the jq extract)
dxs configuration get footprintworkflow <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit ...
dxs configuration upsert footprintworkflow -b <branchId> -D body.json
Phase 7: Verify (optional)
Exercise the slot in the running platform — trigger the entity status change / cartonization / allocation / location-recommendation and confirm the workflow is invoked and the result or before-commit mutation lands as intended. If the app isn't available, re-fetch the config (jq .json extract) and diff against body.json to confirm the push landed.
Pre-Flight Checklist
Walk the full list in references/footprint-workflows.md → Pre-Flight Checklist. Fast version:
configurationTypeId: 23; conventional suffix -footprintWorkflow.json.
- Slot binding real —
workflowDefinitionId + workflowDefinitionName copied from the workflowsMetadata catalog; apiSettingName matches the branch's Footprint setting.
workflowGUID correct — fresh v4 UUID (new workflow, callers repointed), the unchanged existing value (edit), or the legacy workflow's GUID (deliberate drop-in replacement). Never regenerated on an edit.
- Param contract matches the slot — single
Input typed with the slot's input type (usually FootPrintWorkflow.<Slot>InputBaseWL); out-params exactly as the slot dictates (or []).
description ≤ 100 chars, non-empty; accessModifier set; id: 0 for net-new.
- Single
ExecuteCodeActivity node; start points at its id; vars/events/fromBaseConfiguration null.
- Action-tier compliant code —
$flows.<Pkg>.<action> (no functions, no $db), fpds reads, $types.FootPrintWorkflow.*, $utils.isDefined; declared-outParams → return $flow.outParams;.
- Types verified against
contexts or the metadata types.
- Validated against the branch.
Common Mistakes
| Mistake |
Fix |
Invented or mismatched workflowDefinitionId / workflowDefinitionName |
Pull the slot's id/name + inParams/outParams from the workflowsMetadata API and copy verbatim. A wrong/mixed id+name validates clean but never wires. |
Regenerated workflowGUID on an edit, or cloned one from an unrelated config |
The GUID is what callers pass to select this workflow (not part of the slot binding). Fresh v4 UUID for a new workflow (python3 -c "import uuid; print(uuid.uuid4())"); preserve unchanged on edits. Copying a GUID is correct in exactly one case — a drop-in replacement for the workflow that owns it. |
Renamed Input, added in-params, or changed the out-param shape |
The slot owns the signature. Keep single Input: FootPrintWorkflow.<Slot>InputBaseWL + the slot's exact out-params (or []). Mismatch validates clean, breaks the invoke. |
Hand-wrote interfaces for the Input/result |
Read real shapes from dxs configuration contexts footprintworkflow -D body.json; reference $types.FootPrintWorkflow.*. |
Called a function or used $db from the workflow |
Action-tier: call actions via $flows.<Pkg>.<action>; wrap function/$db logic behind an action; use fpds, not cloud datasources. |
Bare return; in a slot that declares outParams |
Use return $flow.outParams; — bare return; breaks the generated body; Validate misses it, Preview catches it. |
Piped get -O envelope.json straight into upsert -D |
jq .json envelope.json > body.json first — the envelope wipes content and carries secrets. |
Raw find/replace on the minified code string |
Edit via Python json.load/json.dump; build with \r\n joins; never restructure surrounding JSON. |
| Deep business logic inline in the node |
Keep the node a thin dispatcher to package actions; matches the library, stays maintainable. |
| Authored against PublishedMain |
Main is readonly: true. Author on a confirmed feature branch (never guess the id). |
After your edit, invoke post-edit-verification to surface description/JSON/schema violations. For a final review, invoke component-validator.
1---2name: footprint-workflows3description: Use when authoring or modifying a Datex Studio footprint-workflow (configurationTypeId=23, CLI type `footprintworkflow`, conventional -footprintWorkflow.json suffix) on a branch — a low-code TypeScript implementation that plugs into a named Footprint platform workflow extension point (Cartonization, Entity Status Change (Before Commit), Allocation Strategy, Recommend License Plate Location, Barcode Parser, …). Owns the platform-fixed signature contract (single `Input: FootPrintWorkflow.<Slot>InputBaseWL`, slot-dictated out-params), the slot binding (workflowDefinitionId/Name discovered via the workflowsMetadata API; workflowGUID is the code callers pass: generate fresh for a new workflow, preserve on edits, reuse the legacy value for a drop-in replacement), the action-tier calling rules, and the thin-dispatcher pattern. Triggers: "create/edit a footprint workflow", "implement the Cartonization workflow", "customize entity status change before commit", "allocation strategy workflow", "recommend location workfl4---56# Footprint Workflows78Author or modify a Datex Studio footprint-workflow (`configurationTypeId: 23`) on a branch — a low-code TypeScript implementation that the **Footprint platform invokes at a named extension point** in its own processing (before an entity status commits, while cartonizing, while planning allocation, while recommending a location, …). It is the modern replacement for the legacy XAML "Datex Workflow" activities. The platform owns the slot, the GUID, and the input/output contract; **you own the body**.910## References1112- [../datex-studio-shared/branch-setup.md](../datex-studio-shared/branch-setup.md) — Branch/connection selection (shared across skills)13- [references/footprint-workflows.md](references/footprint-workflows.md) — Authoritative reference: body shape, the slot binding + workflowsMetadata discovery, the fixed param contract, per-slot field breakdowns, the extension-point catalog, code patterns, CLI lifecycle, pre-flight checklist14- [../datex-studio-shared/configuration-roundtrip.md](../datex-studio-shared/configuration-roundtrip.md) — The `get -O envelope → jq .json → upsert -D` round-trip and the silent-wipe bug it avoids15- [../datex-studio-conventions/file-format.md](../datex-studio-conventions/file-format.md) — `configurationTypeId` table, code-string editing rules, the `return;`-with-outParams rule16- [../datex-studio-conventions/naming-conventions.md](../datex-studio-conventions/naming-conventions.md) — `_workflow` suffix preference; backend types are not bound by the display-name rule17- [../datex-studio-conventions/universal-checklist.md](../datex-studio-conventions/universal-checklist.md) — cross-cutting checks every component must pass18- [../datex-studio-runtime/calling-conventions.md](../datex-studio-runtime/calling-conventions.md) — execution-tier rules; workflow runs action-tier (calls actions via `$flows`, reads `fpds`, no `$db`, no functions)19- [../datex-studio-shared/flow-code-patterns.md](../datex-studio-shared/flow-code-patterns.md) — `$utils.isDefined`, date defaulting, and other flow-code idioms20- [../action-creator/references/actions.md](../action-creator/references/actions.md) — actions are the dispatch target the workflow body calls; same Footprint-server tier21- [../datasource-creator/references/datasources.md](../datasource-creator/references/datasources.md) — `fpds_*` footprint-datasources the workflow reads (action-tier)2223## Dependencies2425- **`requirements-gathering`** skill — invoked to produce a requirements brief if one doesn't already exist in context (which slot, what the custom behavior is, what it dispatches to, which package).26- **`action-creator`** skill — invoked to author the **package actions** the workflow body dispatches to. Keep the workflow node a thin dispatcher; real logic lives in actions (same Footprint-server tier).27- **`function-creator`** skill — invoked when the dispatched logic is better expressed as a function wrapped behind an action (workflows can't call functions directly).28- **`datasource-creator`** skill — invoked when the workflow reads configuration/state through a `fpds_*` footprint-datasource.29- **`type-definition-creator`** skill — invoked when the dispatch targets need package interfaces/enums (the `FootPrintWorkflow.*` input/result types are platform-owned and read from `contexts`, not authored).30- **`impact-analysis`** skill — invoked before changing a workflow that other code depends on, or before renaming the actions/datasources it dispatches to; trace `$flows.<Package>.<action>` call sites rather than grepping inline.3132## CLI Lifecycle3334Workflow authoring goes through `dxs configuration` — the generic CRUD primitive over every platform configuration type. **There is no `dxs workflow` subcommand** and no field-level patching; you build (or fetch + extract) the whole JSON body, edit it, and push the whole thing back. The CLI type identifier is **`footprintworkflow`** (lowercase, matches `ConfigurationEndpoints.normalize_type` output), mapping to `configurationTypeId: 23`. PublishedMain configs are `readonly: true` — author on a **feature branch**.3536**New workflow — discover the slot from the metadata API, assign a fresh GUID** (replacing a legacy37workflow instead? reuse its GUID — see step 2):3839```bash40# 1. Discover the slot: id (= workflowDefinitionId), name, exact inParams/outParams41dxs api GET "/footPrintApiConnections/byName/<connectionName>/workflowsMetadata?applicationId=<branchId>" --raw -O meta.json42jq '.workflowsMetadataJson.workflowDefinitions[] | select(.name=="Cartonization")' meta.json43# 2. Generate a fresh workflowGUID for this new config (new capability; you point callers at it).44# Superseding a legacy workflow on this slot? Reuse ITS GUID instead so existing callers keep working.45python3 -c "import uuid; print(uuid.uuid4())"46# 3. Build body.json: apiSettingName + workflowDefinitionId/Name (step 1) + your fresh GUID,47# the slot's inParams/outParams verbatim, configurationTypeId:23, id:0, your referenceName/title/description/code48# 4. (Optional) read Input/result field shapes — contexts OR meta.json `types`49dxs configuration contexts footprintworkflow -b <branchId> -D body.json50# 5. Validate, THEN upsert — these are two steps, not one. Validate exits 1 when it finds51# errors (read validation_errors, fix body.json, re-run); do not run the upsert on exit 1.52dxs configuration validate footprintworkflow -b <branchId> -D body.json53dxs configuration upsert footprintworkflow -b <branchId> -D body.json54```5556**Edit an existing workflow (round-trip — never skip the jq extract):**5758```bash59dxs configuration get footprintworkflow <configId> -b <branchId> -O envelope.json60jq .json envelope.json > body.json # EXTRACT THE INNER BODY (round-trip footgun guard)61# ... edit nodes[0].stepConfig.executeCodeConfig.code ...62dxs configuration validate footprintworkflow -b <branchId> -D body.json # exit 1 = errors found; fix, do not push63dxs configuration upsert footprintworkflow -b <branchId> -D body.json64```6566### Round-trip rule (critical)6768Never pipe `envelope.json` directly into `dxs configuration upsert` — it silently destroys config content (the envelope carries `id`/`jsonString`/`version`/… that `upsert -D` doesn't expect). Always `jq .json envelope.json > body.json` first. The envelope also carries **Azure app-registration secrets** in `application.applicationDefinition` — the `jq .json` extract drops them; never commit `envelope.json`. See [../datex-studio-shared/configuration-roundtrip.md](../datex-studio-shared/configuration-roundtrip.md).6970## Workflow7172```73[Phase 1: Setup + Requirements]74Follow branch-setup.md for branch/connection selection (feature branch — main is readonly)75 |76[requirements brief in context?]77 YES -> use it NO -> invoke `requirements-gathering`78 |79[Phase 2: Identify the platform slot]80Pick the extension point (Cartonization, Entity Status Change (Before Commit),81Allocation Strategy, Recommend* , Barcode Parser, ...). Consult82references/footprint-workflows.md -> Extension-Point Catalog.83If nothing in the platform *calls* the slot -> a workflow config is inert; stop.84 |85[Phase 3: Bind to the slot]86Pull the workflowsMetadata API for the connection/branch -> copy the slot's87id (=workflowDefinitionId), name, and inParams/outParams VERBATIM. Never88invent the id/name. workflowGUID is NOT in the metadata — it is the code callers89pass: generate fresh for a NEW workflow, preserve it on edits, and reuse the90legacy GUID when shipping a drop-in replacement for an existing workflow.91 |92[Phase 4: Author the body]93Build body.json:94 - Slot binding (Phase 3): apiSettingName + workflowDefinitionId/Name +95 your workflowGUID + configurationTypeId:23 + start:"step1"96 - Fixed param contract: single Input: FootPrintWorkflow.<Slot>InputBaseWL;97 out-params exactly as the slot dictates (or [] for before-commit mutation)98 - Single ExecuteCodeActivity node; code is a THIN DISPATCHER to package99 actions ($flows.<Pkg>.<action>) and fpds reads; map results into100 $flow.outParams; action-tier rules (no $db, no functions)101 - id:0, accessModifier:public, description <=100 chars, vars/events null102 |103[Phase 5: Discover types + validate]104dxs configuration contexts footprintworkflow -b <branchId> -D body.json105 -> confirm $types.FootPrintWorkflow.<X>InputBaseWL + result fields you use106dxs configuration validate footprintworkflow -b <branchId> -D body.json107 |108[Phase 6: Push]109 CREATE -> dxs configuration upsert footprintworkflow -b <branchId> -D body.json110 MODIFY -> get -O envelope -> jq .json -> edit -> upsert (round-trip)111 |112[Phase 7: Verify (optional)]113Exercise the slot in the running platform; confirm the workflow is invoked and114the result/mutation lands. Re-fetch (jq .json) and diff against body.json.115 |116[invoke `post-edit-verification`; then `component-validator`]117```118119## Phase Details120121### Phase 1: Setup + Requirements1221231. Follow [../datex-studio-shared/branch-setup.md](../datex-studio-shared/branch-setup.md). **Never assume a branch ID** — confirm with the user. Author on a **feature branch**; PublishedMain workflow configs are `readonly: true`.1242. Check for a **requirements brief** in context. The brief should establish: which platform slot, the custom behavior, what package actions/datasources it dispatches to, and the target package. No brief → invoke `requirements-gathering`.125126### Phase 2: Identify the platform slot127128A footprint-workflow only runs if the Footprint platform *calls* its slot. Consult [references/footprint-workflows.md → Purpose & When to Use](references/footprint-workflows.md#purpose--when-to-use) and the [Extension-Point Catalog](references/footprint-workflows.md#extension-point-catalog). If the desired behavior isn't triggered by one of the named workflow slots, this is the wrong component type — a plain action/function/datasource is what's needed. If the signature should be *yours* to design, it's a function/action, not a workflow.129130### Phase 3: Bind to the slot131132The slot binding has two independent parts:1331341. **Slot identity + signature (from the platform).** Pull the [workflowsMetadata API](references/footprint-workflows.md#discovering-the-slot-catalog-authoritative-source) for the Footprint connection and branch:135 ```bash136 dxs api GET "/footPrintApiConnections/byName/<connectionName>/workflowsMetadata?applicationId=<branchId>" --raw -O meta.json137 jq '.workflowsMetadataJson.workflowDefinitions[] | {id, name, inParams, outParams}' meta.json138 ```139 Copy the slot's `id` → `workflowDefinitionId`, `name` → `workflowDefinitionName`, and its `inParams`/`outParams` **verbatim**. `<connectionName>` is a real Footprint connection (e.g. `DSV`), not the `apiSettingName` value. Set `apiSettingName` from the branch's Footprint setting. **Never invent the id/name.**1402. **`workflowGUID` (the code callers pass).** It is **not** in the metadata, so nothing in the catalog dictates it — but callers do: the value reaches the server as `ProcessingStrategyWorkflowCode` / `AllocationStrategyWorkflowId` and selects which implementation of the slot runs. **Generate a fresh v4 UUID** for a genuinely new workflow (`python3 -c "import uuid; print(uuid.uuid4())"`) and then point the callers at it; **preserve the existing GUID unchanged** when editing (the `jq .json` round-trip keeps it); **reuse the legacy GUID** when your config is a drop-in replacement for an existing implementation whose callers must not change. Never regenerate on an edit.141142### Phase 4: Author the body143144Build `body.json` from [references/footprint-workflows.md → Minimal Valid Skeleton](references/footprint-workflows.md#minimal-valid-skeleton). Key points:1451461. **File basics** per the **Pre-Flight Checklist** below + [universal checklist](../datex-studio-conventions/universal-checklist.md): `configurationTypeId: 23`, `id: 0`, `description` ≤ 100 chars, `accessModifier` set, package matches the feature.1472. **Param contract is the platform's, not yours.** Single `Input` in-param typed `FootPrintWorkflow.<Slot>InputBaseWL`; out-params exactly as the slot dictates (one object, a collection, or `[]` for before-commit mutation). Don't rename `Input`, add params, or change the out-param shape.1483. **Single `ExecuteCodeActivity` node.** `start: "step1"`, node `id: "step1"`, `decisionConfig: null`. Multi-node graphs are possible but unused in the library — single step unless you have a concrete reason.1494. **Code is a thin dispatcher.** Translate `$flow.inParams.Input` into `$flows.<Package>.<action>` calls and `fpds` reads; map results onto `$flow.outParams.<Name>`. Action-tier rules: no `$db`, no direct function calls, `fpds` (not cloud `-datasource.json`). Declared-`outParams` slots use `return $flow.outParams;`, never bare `return;`. See [references/footprint-workflows.md → Code Patterns](references/footprint-workflows.md#code-patterns--the-body-is-a-thin-dispatcher).1505. **`vars` / `events` / `fromBaseConfiguration` stay `null`.**151152### Phase 5: Discover types + validate153154```bash155dxs configuration contexts footprintworkflow -b <branchId> -D body.json156dxs configuration validate footprintworkflow -b <branchId> -D body.json157```158159`contexts` returns the `$types.FootPrintWorkflow.*` IntelliSense surface, and its `flowContext` echoes your declared signature as a typed `IFlow` — a quick check that `$flow.inParams.Input` / `$flow.outParams.*` resolve to the slot's types. Confirm every field you reference exists (don't hand-roll the platform types). Three discovery sources, by need (see [references → Discovering the type surface](references/footprint-workflows.md#discovering-the-type-surface)):160161- **`FootPrintWorkflow.*` platform types** (the `Input`/result shapes) — `contexts`, or the `workflowsMetadata` API's `types` array (structured JSON, easier to query).162- **`$types.<Package>.*` package types** your dispatch code uses (e.g. `$types.Utilities.e_awi_scopes.Cartonization`) — `dxs configuration nomenclature -b <branchId>` (filter with `--package`/`--kind enum`/`--search`; enums list members in `constantValues`).163164`validate` catches structural/required-field errors but **not** a wrong slot binding (`workflowDefinitionId`/name), a signature that doesn't match the slot, or action-tier violations — walk the Pre-Flight Checklist for those.165166### Phase 6: Push167168```bash169# New170dxs configuration upsert footprintworkflow -b <branchId> -D body.json171# Modify-existing (round-trip — never skip the jq extract)172dxs configuration get footprintworkflow <configId> -b <branchId> -O envelope.json173jq .json envelope.json > body.json174# ... edit ...175dxs configuration upsert footprintworkflow -b <branchId> -D body.json176```177178### Phase 7: Verify (optional)179180Exercise the slot in the running platform — trigger the entity status change / cartonization / allocation / location-recommendation and confirm the workflow is invoked and the result or before-commit mutation lands as intended. If the app isn't available, re-fetch the config (`jq .json` extract) and diff against `body.json` to confirm the push landed.181182## Pre-Flight Checklist183184Walk the full list in [references/footprint-workflows.md → Pre-Flight Checklist](references/footprint-workflows.md#pre-flight-checklist). Fast version:1851861. **`configurationTypeId: 23`**; conventional suffix `-footprintWorkflow.json`.1872. **Slot binding real** — `workflowDefinitionId` + `workflowDefinitionName` copied from the workflowsMetadata catalog; `apiSettingName` matches the branch's Footprint setting.1883. **`workflowGUID` correct** — fresh v4 UUID (new workflow, callers repointed), the unchanged existing value (edit), or the legacy workflow's GUID (deliberate drop-in replacement). Never regenerated on an edit.1894. **Param contract matches the slot** — single `Input` typed with the slot's input type (usually `FootPrintWorkflow.<Slot>InputBaseWL`); out-params exactly as the slot dictates (or `[]`).1905. **`description` ≤ 100 chars, non-empty;** `accessModifier` set; `id: 0` for net-new.1916. **Single `ExecuteCodeActivity` node;** `start` points at its `id`; `vars`/`events`/`fromBaseConfiguration` `null`.1927. **Action-tier compliant code** — `$flows.<Pkg>.<action>` (no functions, no `$db`), `fpds` reads, `$types.FootPrintWorkflow.*`, `$utils.isDefined`; declared-`outParams` → `return $flow.outParams;`.1938. **Types verified** against `contexts` or the metadata `types`.1949. **Validated** against the branch.195196## Common Mistakes197198| Mistake | Fix |199|---|---|200| Invented or mismatched `workflowDefinitionId` / `workflowDefinitionName` | Pull the slot's `id`/`name` + `inParams`/`outParams` from the `workflowsMetadata` API and copy verbatim. A wrong/mixed id+name validates clean but never wires. |201| Regenerated `workflowGUID` on an edit, or cloned one from an unrelated config | The GUID is what callers pass to select this workflow (not part of the slot binding). Fresh v4 UUID for a new workflow (`python3 -c "import uuid; print(uuid.uuid4())"`); preserve unchanged on edits. Copying a GUID is correct in exactly one case — a drop-in replacement for the workflow that owns it. |202| Renamed `Input`, added in-params, or changed the out-param shape | The slot owns the signature. Keep single `Input: FootPrintWorkflow.<Slot>InputBaseWL` + the slot's exact out-params (or `[]`). Mismatch validates clean, breaks the invoke. |203| Hand-wrote interfaces for the `Input`/result | Read real shapes from `dxs configuration contexts footprintworkflow -D body.json`; reference `$types.FootPrintWorkflow.*`. |204| Called a function or used `$db` from the workflow | Action-tier: call actions via `$flows.<Pkg>.<action>`; wrap function/`$db` logic behind an action; use `fpds`, not cloud datasources. |205| Bare `return;` in a slot that declares `outParams` | Use `return $flow.outParams;` — bare `return;` breaks the generated body; Validate misses it, Preview catches it. |206| Piped `get -O envelope.json` straight into `upsert -D` | `jq .json envelope.json > body.json` first — the envelope wipes content and carries secrets. |207| Raw find/replace on the minified `code` string | Edit via Python `json.load`/`json.dump`; build with `\r\n` joins; never restructure surrounding JSON. |208| Deep business logic inline in the node | Keep the node a thin dispatcher to package actions; matches the library, stays maintainable. |209| Authored against PublishedMain | Main is `readonly: true`. Author on a confirmed feature branch (never guess the id). |210211**After your edit, invoke `post-edit-verification` to surface description/JSON/schema violations. For a final review, invoke `component-validator`.**