mcpField
F
footprint-workflows

footprint-workflows

github.com/datexDevelopment88 installs
Repository

About footprint-workflows

Footprint Workflows is a skill for authoring and modifying Datex Studio footprint-workflow configurations in Footprint. It guides developers through binding a low-code TypeScript body to a platform-defined extension point, validating it, and publishing it on a feature branch.

Key features

  1. 01Binds workflows to platform-defined extension points
  2. 02Uses metadata to discover slot parameters
  3. 03Authors low-code TypeScript workflow bodies
  4. 04Validates and upserts configurations with `dxs`

Use cases

  1. 01Implementing custom logic at a Footprint processing extension point
  2. 02Editing a workflow while preserving its configuration and GUID
  3. 03Checking workflow structure before pushing a configuration

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/datex/skills --skill footprint-workflows

After installing, type/footprint-workflowsin Claude Code to confirm it loaded.

Bundled filesreferences/footprint-workflows.md

Details

From the project README

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

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
  1. 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.
  2. 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:

  1. 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.
  2. 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:

  1. File basics per the Pre-Flight Checklist below + universal checklist: configurationTypeId: 23, id: 0, description ≤ 100 chars, accessModifier set, package matches the feature.
  2. 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.
  3. 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.
  4. 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.
  5. 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:

  1. configurationTypeId: 23; conventional suffix -footprintWorkflow.json.
  2. Slot binding real — workflowDefinitionId + workflowDefinitionName copied from the workflowsMetadata catalog; apiSettingName matches the branch's Footprint setting.
  3. 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.
  4. 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 []).
  5. description ≤ 100 chars, non-empty; accessModifier set; id: 0 for net-new.
  6. Single ExecuteCodeActivity node; start points at its id; vars/events/fromBaseConfiguration null.
  7. Action-tier compliant code — $flows.<Pkg>.<action> (no functions, no $db), fpds reads, $types.FootPrintWorkflow.*, $utils.isDefined; declared-outParams → return $flow.outParams;.
  8. Types verified against contexts or the metadata types.
  9. Validated against the branch.

Common Mistakes

MistakeFix
Invented or mismatched workflowDefinitionId / workflowDefinitionNamePull 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 configThe 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 shapeThe 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/resultRead real shapes from dxs configuration contexts footprintworkflow -D body.json; reference $types.FootPrintWorkflow.*.
Called a function or used $db from the workflowAction-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 outParamsUse return $flow.outParams; — bare return; breaks the generated body; Validate misses it, Preview catches it.
Piped get -O envelope.json straight into upsert -Djq .json envelope.json > body.json first — the envelope wipes content and carries secrets.
Raw find/replace on the minified code stringEdit via Python json.load/json.dump; build with \r\n joins; never restructure surrounding JSON.
Deep business logic inline in the nodeKeep the node a thin dispatcher to package actions; matches the library, stays maintainable.
Authored against PublishedMainMain 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.

Comments

Markdown supported