Skip to content

feat(core): canonical identity of CWL processes - #1114

Draft
ryuwd wants to merge 1 commit into
DIRACGrid:mainfrom
ryuwd:cwl-1-canonical
Draft

ryuwd wants to merge 1 commit into
DIRACGrid:mainfrom
ryuwd:cwl-1-canonical

Conversation

@ryuwd

@ryuwd ryuwd commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Identifies each CWL process by hash_json of the process as submitted in a cwltool --pack document, with each step's run replaced by its child's identity, so a workflow's identity covers its children's. Nothing is normalised: another cwltool version, an edited doc or a renamed file only costs a duplicate row, and DiracX never rewrites what the user tested. check_self_contained refuses documents that need anything from outside themselves ($ keys other than $graph/$namespaces, a run outside the $graph, File/Directory given by path or location), and subgraph returns the document one transformation runs. A worked example is in docs/dev/explanations/cwl-process-identity.md.

Closes #1106. Part of #1080.

@chrisburr chrisburr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've spent a while going through this and I get the impression it would be better/simpler to do cwltool --pack? In DiracX we would then only need to:

  1. Client: run cwltool --pack, take the dirac: hints out into the transformation fields and drop them from the document.
  2. Server: split the $graph into components, replace each run: #x with the child's hash and hash each component with hash_json.
  3. Both: run one shared check_self_contained() (stdlib only, in diracx-core) so the document never needs anything from outside once it reaches DiracX:
    • no $ keys except $namespaces ($schemas gets fetched)
    • every run is a #id within the $graph
    • requirement/hint classes from an allowlist (cwltool tries to fetch unknown ones)
    • File/Directory literals only with inline contents (pack turns a local path into the submitter's absolute file:/// path and keeps URLs)
    • no dockerLoad/dockerImport/dockerFile

The identity is then the hash of what was submitted rather than of a normalised form. A different cwltool version, a renamed file or an edited doc gives a new id, but that only costs a duplicate row. In exchange we drop the rules that reimplement schema-salad's resolution.

@DIRACGridBot
DIRACGridBot marked this pull request as draft November 4, 2026 13:19
@aldbr

aldbr commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

I haven't had a look at the PR yet, but your suggestion makes sense to me.

  1. Client: run cwltool --pack, take the dirac: hints out into the transformation fields and drop them from the document.

I think packing the cwl from the client was the idea we discussed during the workshop, am I wrong?
Note: cwl_utils.pack.pack would work in python (instead of running cwltool --pack).
Though, it's should not be part of this PR, should it?

  • no dockerLoad/dockerImport/dockerFile

Note that @natthan-pigoux made it possible to get docker images from CVMFS if I remember correctly. Can you remind us how it works please?

@natthan-pigoux

Copy link
Copy Markdown
Contributor

Note that @natthan-pigoux made it possible to get docker images from CVMFS if I remember correctly. Can you remind us how it works please?

It was not directly targeting CVMFS but it was for dockerPull: to handle unpacked images (that the way images are stored on cvmfs). So if you target an unpacked image from CVMFS through dockerPull: it works.

The client submits a document packed by cwltool --pack: a $graph of
processes in which a step's run names another entry or holds an inline
body. A process is identified by hash_json of the process as submitted,
with each run replaced by its child's identity, so a workflow's identity
covers its children's (a Merkle tree). Nothing is normalised: another
cwltool version, an edited doc or a renamed file gives another identity,
which only costs a duplicate row, and DiracX never rewrites what the user
tested.

check_self_contained refuses a document that needs anything from
outside itself: a $ key other than the top-level $graph and
$namespaces, a run that is not an entry of the $graph or an inline
body, and a File or Directory given by path or location rather than
inline contents. subgraph returns the document one entry runs.

Closes DIRACGrid#1106
Part of DIRACGrid#1080
@ryuwd
ryuwd requested a review from chrisburr November 4, 2026 16:33
@ryuwd
ryuwd marked this pull request as ready for review November 5, 2026 09:20
@aldbr

aldbr commented Nov 6, 2026

Copy link
Copy Markdown
Contributor
  • requirement/hint classes from an allowlist (cwltool tries to fetch unknown ones)
  • no dockerLoad/dockerImport/dockerFile

The following points are listed in #1107 so I assume they are expected to come later.

@aldbr aldbr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you just check what would happen with the following examples please?
(this is kind of niche, but it can happen if you would try to submit a CWL process without passing through the diracx client and directly go through the REST API for instance)

  • duplicate ids:
{
    "cwlVersion": "v1.2",
    "$graph": [
      {"id": "#main", "class": "Workflow",
       "steps": [{"id": "sim",   "run": "#gauss.cwl"},
                 {"id": "merge", "run": "#merge.cwl"}]},
      {"id": "#gauss.cwl", "class": "Workflow",
       "steps": [{"id": "run", "run": "#lb-run-app.cwl"}]},
      {"id": "#lb-run-app.cwl", "class": "CommandLineTool",
       "baseCommand": ["lb-run", "Gauss/v56r1"]},          // entry A
      {"id": "#merge.cwl", "class": "Workflow",
       "steps": [{"id": "run", "run": "#lb-run-app.cwl"}]},
      {"id": "#lb-run-app.cwl", "class": "CommandLineTool",
       "baseCommand": ["lb-run", "--verbose", "LHCb/v57r2"]} // entry B, same id
    ]  
  }
  • missing ids:
{
    "cwlVersion": "v1.2",
    "$graph": [
      {"class": "CommandLineTool", "baseCommand": ["echo", "first"]},
      {"class": "CommandLineTool", "baseCommand": ["echo", "second"]}
    ] 
  }


:func:`check_self_contained` makes sure that a document needs nothing from
outside itself, so that neither the services nor a runner fetch anything.
See ``docs/dev/explanations/cwl-process-store.md`` for a worked example.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
See ``docs/dev/explanations/cwl-process-store.md`` for a worked example.
See ``docs/dev/explanations/cwl-process-identity.md`` for a worked example.

@DIRACGridBot
DIRACGridBot marked this pull request as draft November 6, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Transformation System] Content identity of packed CWL processes

5 participants