klej 0.0.1__tar.gz

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.
klej-0.0.1/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
klej-0.0.1/MANIFEST.in ADDED
@@ -0,0 +1 @@
1
+ prune tests
klej-0.0.1/NOTICE ADDED
@@ -0,0 +1,2 @@
1
+ klej
2
+ Copyright 2026 Maciej Falkiewicz
klej-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,441 @@
1
+ Metadata-Version: 2.4
2
+ Name: klej
3
+ Version: 0.0.1
4
+ Summary: The missing glue between DVC and Slurm
5
+ Author: Maciej Falkiewicz
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Documentation, https://github.com/macio232/klej#readme
8
+ Project-URL: Issues, https://github.com/macio232/klej/issues
9
+ Project-URL: Source, https://github.com/macio232/klej
10
+ Keywords: workflow,scheduling,slurm,hpc,dvc
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Natural Language :: English
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ License-File: NOTICE
21
+ Requires-Dist: dawgz==2.7.0
22
+ Requires-Dist: dvc==3.66.0
23
+ Requires-Dist: pyyaml>=6.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: ruff>=0.16.6; extra == "dev"
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=8.0.0; extra == "test"
28
+ Dynamic: license-file
29
+
30
+ # klej
31
+
32
+ `klej` is the missing glue between
33
+ [DVC](https://dvc.org/) and [Slurm](https://slurm.schedmd.com/slurm.html). It
34
+ turns selected DVC stages into a dependency-aware [DAWGZ](https://github.com/francois-rozet/dawgz) workflow and
35
+ executes each stage as a separate Slurm job.
36
+
37
+ The name means “glue” in Polish.
38
+
39
+ ## Installation
40
+
41
+ `klej` requires Python 3.11 or newer.
42
+
43
+ Install the pre-alpha release from PyPI as an isolated command with
44
+ [uv](https://docs.astral.sh/uv/):
45
+
46
+ ```console
47
+ uv tool install klej==0.0.1
48
+ ```
49
+
50
+ Alternatively, install it into the active Python environment with pip:
51
+
52
+ ```console
53
+ python -m pip install klej==0.0.1
54
+ ```
55
+
56
+ To install manually from a source checkout, create a virtual environment and
57
+ install the local project:
58
+
59
+ ```console
60
+ git clone https://github.com/macio232/klej.git
61
+ cd klej
62
+ python -m venv .venv
63
+ source .venv/bin/activate
64
+ python -m pip install .
65
+ ```
66
+
67
+ This is an early release intended for evaluation and research workflows, not
68
+ production use.
69
+
70
+ ## Project status and safety
71
+
72
+ `klej` is an early-stage research tool. It may contain bugs, and its commands,
73
+ configuration, and Python interfaces may change without backward
74
+ compatibility. Do not rely on it for production-critical workloads without
75
+ independent testing.
76
+
77
+ Submitting a pipeline can create many jobs that run in parallel and may
78
+ consume substantial cluster resources or incur significant cost. Review the
79
+ plan with `--dry-run`, understand your cluster's quotas and billing, and
80
+ monitor submitted jobs. You are responsible for the workloads and costs
81
+ created by your use of `klej`; use it at your own risk.
82
+
83
+ ## Usage
84
+
85
+ Run from a DVC repository:
86
+
87
+ ```console
88
+ klej repro path/to/dvc.yaml --dry-run
89
+ klej repro path/to/dvc.yaml
90
+ klej repro [targets ...]
91
+ klej repro models/dvc.yaml:train --downstream
92
+ klej repro pipelines/ -R
93
+ klej status
94
+ klej status WORKFLOW_ID
95
+ klej status --failed
96
+ klej status --finished
97
+ klej logs WORKFLOW_ID JOB --tail 100 --follow
98
+ klej logs --clear WORKFLOW_ID
99
+ klej logs --clear-all --force
100
+ klej cancel WORKFLOW_ID --dry-run
101
+ klej lock status
102
+ ```
103
+
104
+ Targets default to `./dvc.yaml` and use the same forms as `dvc repro`: stage
105
+ names, `dvc.yaml` and `.dvc` paths, `path/dvc.yaml:stage`, foreach or matrix
106
+ expansions, glob patterns with `--glob`, and directories with `-R`. DVC
107
+ dependencies in other pipeline files are included automatically.
108
+
109
+ Regular, foreach, matrix-expanded, frozen, import, `.dvc`, and
110
+ checkpoint-marked stages are supported according to pinned DVC 3.66
111
+ `dvc repro` behavior. Experiment checkpoint snapshots and resume behavior are
112
+ not provided.
113
+
114
+ The supported DVC selection and execution flags are `-f/--force`,
115
+ `-i/--interactive`, `-s/--single-item`, `-p/--pipeline`,
116
+ `-P/--all-pipelines`, `-R/--recursive`, `--downstream`,
117
+ `--force-downstream`, `--allow-missing`, `--glob`, `--no-run-cache`,
118
+ `--ignore-errors`, `--no-commit`, `--dry`, and `-v/--verbose`. `--dry-run` is
119
+ an alias for `--dry`. `--pull` and `-q/--quiet` are not supported.
120
+ `-k/--keep-going` is unnecessary: klej always continues independent Slurm
121
+ branches while failed dependencies prevent their downstream jobs from
122
+ running. `--ignore-errors` changes those dependencies from Slurm `afterok` to
123
+ `afterany`; failures remain visible in workflow status.
124
+
125
+ `-v/--verbose` reports repository and target resolution, selected and skipped
126
+ stages, effective configuration files, profiles, dependencies, runner and
127
+ reservation identifiers, and submitted Slurm IDs. It also enables DVC debug
128
+ logging and klej cache/metadata/lock diagnostics in job logs. Resolved
129
+ environment values are not printed.
130
+
131
+ An explicit `--config` is one complete configuration used for every selected
132
+ pipeline. Otherwise, repository-root `klej.yaml` is the base and a
133
+ `klej.yaml` beside each pipeline may partially override it. Named `profiles`
134
+ and `stages` are merged; local `commit`, `klej`, `environment`, and
135
+ `wrapper` values replace the base values. With one explicit configuration,
136
+ identically named stages in different pipeline files necessarily use the same
137
+ profile mapping. Multi-pipeline runs create one cleanup job, so all effective
138
+ pipeline configurations must resolve to the same klej-operation profile and
139
+ environment. Align those settings or use `--config` when they differ.
140
+
141
+ `klej cancel` queries Slurm and cancels only jobs that are still cancellable.
142
+ Its output reports counts rather than printing one line per job.
143
+
144
+ Dry-run output reports projected logical and physical job totals for the whole
145
+ workflow, including commit and reservation-cleanup jobs. A normal stage is one
146
+ logical and physical job. A Slurm stage with multiple profiles remains one
147
+ logical job but produces one physical attempt per profile plus a success gate;
148
+ its stage detail shows that expansion explicitly. No Slurm arrays are used.
149
+
150
+ ```yaml
151
+ profiles:
152
+ cpu:
153
+ account: my-account
154
+ cpus: 4
155
+ mem: 16G
156
+ gpu:
157
+ account: my-account
158
+ gres: gpu:1
159
+ environment:
160
+ APPTAINER_NV: 1
161
+
162
+ stages:
163
+ prepare: cpu
164
+ # Competing Slurm requests for one logical execution.
165
+ train: [gpu, cpu]
166
+
167
+ commit: cpu
168
+ klej: cpu
169
+
170
+ environment:
171
+ APPTAINER_NV: 0
172
+
173
+ # The wrapper receives the runner command as one final argument.
174
+ wrapper: [./apptainer/run.sh]
175
+ ```
176
+
177
+ A stage may name one profile or an ordered, non-empty list of distinct
178
+ profiles. With two or more profiles on Slurm, klej submits one attempt per
179
+ profile; the first allocation to atomically claim the stage runs the wrapper
180
+ and payload. Other attempts exit with status 75 without entering either. The
181
+ claim is permanent, so a failing winner fails the logical stage and does not
182
+ fall back to another profile. At-most-once applies to entering the configured
183
+ wrapper and user payload, not to allocation: multiple Slurm allocations may
184
+ briefly start klej's coordinator. Correctness therefore requires a filesystem
185
+ shared by the submission host and every compute node, with atomic directory
186
+ creation and atomic file replacement. The local backend runs the stage once
187
+ with the first profile; scalar mappings and one-item lists retain the
188
+ single-job behavior.
189
+
190
+ Alternative attempts are submitted held. klej records every physical job ID
191
+ in the workflow plan and target reservation before releasing them. The
192
+ winning attempt best-effort cancels its siblings; reservation cleanup retries
193
+ active registered physical jobs other than itself, waits for Slurm's
194
+ configured `KillWait` plus five seconds after any cancellation, and confirms
195
+ they have left `squeue` before releasing the reservation. If the configuration
196
+ cannot be read, cleanup warns and uses Slurm's 30-second `KillWait` default. A
197
+ query or cancellation failure, or a job still present after the wait, fails
198
+ cleanup and retains the reservation for inspection and explicit lock
199
+ recovery. If submission or registration fails after `sbatch` may have run,
200
+ klej cancels all known jobs, including cleanup, and retains the target
201
+ reservation for explicit lock recovery. Once all physical IDs are registered,
202
+ a later submission failure cancels every other job but preserves cleanup so it
203
+ can release the reservation safely.
204
+
205
+ Version-2 workflow records are readable from their initial planned state, even
206
+ before any Slurm ID exists. Cancellation and submission recovery combine IDs
207
+ from the physical plan and the DAWGZ scheduler record so a partially persisted
208
+ submission does not leave a known job behind.
209
+
210
+ The opt-in
211
+ [`alternative-executors-smoke`](examples/alternative-executors-smoke)
212
+ example provides a low-cost real-cluster verification pipeline. It records
213
+ wrapper and payload entry for a successful race and an intentionally failing
214
+ winner, with commands for inspecting logical and physical status and logs.
215
+ Cluster policies and accounting differ, so review its profiles, quotas, and
216
+ costs before submitting it.
217
+
218
+ ## Commit modes
219
+
220
+ By default, `klej repro` creates one commit job after all leaf compute jobs
221
+ finish. That job commits the selected `dvc.yaml` using the `commit` profile
222
+ from `klej.yaml`.
223
+
224
+ Three mutually exclusive flags provide alternative metadata placement:
225
+
226
+ * `--commit-per-stage` runs `dvc commit` for each stage target inside its
227
+ compute job. The commit therefore uses the stage's profile.
228
+ * `--separate-commit-jobs` creates a dedicated commit job after each compute
229
+ job. These jobs use the `commit` profile, and downstream computation waits
230
+ for the upstream commit job to finish.
231
+ * `--no-dvc-metadata` executes stages without updating DVC metadata or caches.
232
+
233
+ Independently, DVC-compatible `--no-commit` still updates `dvc.lock` but does
234
+ not cache outputs or create run-cache entries.
235
+
236
+ ```console
237
+ # One final commit job (default)
238
+ klej repro path/to/dvc.yaml
239
+
240
+ # Commit each target in its compute job
241
+ klej repro path/to/dvc.yaml --commit-per-stage
242
+
243
+ # Commit each target in a separate job
244
+ klej repro path/to/dvc.yaml --separate-commit-jobs
245
+
246
+ # Update dvc.lock without caching outputs
247
+ klej repro path/to/dvc.yaml --no-commit
248
+
249
+ # Do not update DVC metadata
250
+ klej repro path/to/dvc.yaml --no-dvc-metadata
251
+ ```
252
+
253
+ All commits are serialized through klej's repository-wide commit lock and
254
+ invoke `dvc commit --force`.
255
+
256
+ ## Execution environment
257
+
258
+ klej installs an immutable, standalone compute runner under `.klej/runners/`
259
+ in the directory where the command is run and keeps shared coordination data
260
+ under `.klej/locks/` there. Add `.klej/` to the consumer repository's ignore
261
+ rules. The repository must be visible at the same path to submission and
262
+ compute nodes. Containers need Python and the pinned DVC version, but do not
263
+ need klej installed.
264
+
265
+ ## Wrapper examples
266
+
267
+ Environment values can refer to submission-side variables as `$NAME` or
268
+ `${NAME}`. A profile's environment is applied after the global environment.
269
+ The wrapper runs from the DVC repository root and is used only with Slurm.
270
+
271
+ Set `wrapper` to an executable that prepares the compute-node environment and
272
+ runs its final argument as a shell command. For example, put one of the scripts
273
+ below in the repository and reference it from `klej.yaml`:
274
+
275
+ ```yaml
276
+ wrapper: [./wrappers/run.sh]
277
+ ```
278
+
279
+ Make the script executable with `chmod +x wrappers/run.sh`. The environment
280
+ must provide Python and the pinned DVC version.
281
+
282
+ An Apptainer wrapper can bind the repository at the same path inside the
283
+ container:
284
+
285
+ ```bash
286
+ #!/usr/bin/env bash
287
+ set -euo pipefail
288
+
289
+ readonly PROJECT_DIR="$(pwd -P)"
290
+ readonly IMAGE="${PROJECT_DIR}/containers/project.sif"
291
+
292
+ [[ -f "${IMAGE}" ]] || {
293
+ printf 'container image not found: %s\n' "${IMAGE}" >&2
294
+ exit 1
295
+ }
296
+
297
+ exec apptainer exec \
298
+ --bind "${PROJECT_DIR}:${PROJECT_DIR}" \
299
+ --pwd "${PROJECT_DIR}" \
300
+ "${IMAGE}" bash -c "$1"
301
+ ```
302
+
303
+ For a Conda environment:
304
+
305
+ ```bash
306
+ #!/usr/bin/env bash
307
+ set -euo pipefail
308
+
309
+ exec conda run --no-capture-output -n my-environment bash -c "$1"
310
+ ```
311
+
312
+ For a virtual environment stored in the repository:
313
+
314
+ ```bash
315
+ #!/usr/bin/env bash
316
+ set -euo pipefail
317
+
318
+ source .venv/bin/activate
319
+ exec bash -c "$1"
320
+ ```
321
+
322
+ The equivalent wrapper using uv is:
323
+
324
+ ```bash
325
+ #!/usr/bin/env bash
326
+ set -euo pipefail
327
+
328
+ exec uv run -- bash -c "$1"
329
+ ```
330
+
331
+ To load environment modules provided by the cluster:
332
+
333
+ ```bash
334
+ #!/usr/bin/env bash
335
+ set -euo pipefail
336
+
337
+ module load python/3.12 cuda/12.4
338
+ exec bash -c "$1"
339
+ ```
340
+
341
+ Replace the example environment names, image path, and module versions with
342
+ those available on the cluster. Add `--nv` to `apptainer exec` when the
343
+ container needs NVIDIA GPU access.
344
+
345
+ ## Local backend
346
+
347
+ `--backend local` is intended for development and CI validation of klej's
348
+ planning and execution path. It is not a full alternative to `dvc repro` and
349
+ does not promise Slurm backend parity.
350
+
351
+ ## Examples
352
+
353
+ Runnable pipelines are available in [`examples`](examples/README.md). Each
354
+ example is an independent DVC repository with its own `dvc.yaml` and
355
+ `klej.yaml`.
356
+
357
+ ## Workflow status
358
+
359
+ `klej status` lists active Slurm workflows recorded in the current `.dawgz`
360
+ directory. A workflow is active while any job is pending or running. The
361
+ summary shows the workflow ID, DVC pipeline, and counts of pending, running,
362
+ succeeded, failed, cancelled, and unknown jobs. Counts include compute,
363
+ commit, and reservation-cleanup jobs. Local-backend workflows are omitted.
364
+
365
+ `klej status WORKFLOW_ID` prints the aggregate state and every DAWGZ job in
366
+ stable zero-based order. It deliberately does not truncate or page large
367
+ foreach or matrix workflows. New compute jobs are identified by their exact
368
+ expanded DVC target, such as `dvc.yaml:simulate@0`; internal jobs retain their
369
+ descriptive DAWGZ name. Older records without exact-target metadata remain
370
+ readable using their recorded names. The `START TIME` column contains Slurm's
371
+ unmodified accounting start date-time, or `-` when it is unavailable. Rows
372
+ with known start times are ordered from oldest to newest; missing start times
373
+ appear last in stable DAWGZ order.
374
+
375
+ For alternative resource profiles, status shows one logical row and summary
376
+ count per stage. The winning attempt and success gate determine that row;
377
+ losing attempts do not count as failed workloads. Cancelling a claimed stage
378
+ is reported as cancelled unless its payload failure was already recorded or
379
+ the claimant failed in Slurm. Use
380
+ `klej status WORKFLOW_ID --physical` to append every attempt, gate, commit, and
381
+ cleanup job with its raw Slurm state and exit code. Losing siblings appear as
382
+ `superseded`. Workflow terminality and safe record clearing still account for
383
+ all physical jobs, including rows hidden from the default logical view.
384
+
385
+ `klej status --failed` lists terminal Slurm workflows containing at least one
386
+ failed or cancelled job. A workflow containing an unknown job is not known to
387
+ be terminal. Slurm accounting has a cluster-defined retention window: once a
388
+ job disappears from `sacct`, klej reports it as unknown, so sufficiently old
389
+ failed workflows may no longer appear in this listing.
390
+
391
+ `klej status --finished` lists every recorded workflow known to be terminal,
392
+ including local-backend records, with an aggregate status of `succeeded`,
393
+ `failed`, or `cancelled`. Its `SUBMISSION` column contains the DAWGZ workflow
394
+ submission date-time, and rows are ordered from oldest to newest. Legacy
395
+ records without that timestamp show `-` after timestamped workflows. Pending,
396
+ running, and unknown workflows are omitted.
397
+
398
+ Slurm states are normalized to pending, running, succeeded, failed, cancelled,
399
+ or unknown while the complete scheduler value remains visible in detailed
400
+ status. For example, `COMPLETED+` is succeeded, `CANCELLED by 12345` is
401
+ cancelled, and timeout, out-of-memory, and node failures are failed.
402
+
403
+ ## Job logs
404
+
405
+ `klej logs WORKFLOW_ID JOB` writes the currently available DAWGZ log directly
406
+ to standard output without a heading. `JOB` may be the zero-based index from
407
+ detailed status, an exact Slurm ID, or the exact displayed target/name when it
408
+ is unique. Internal jobs, including separate commit jobs, use their job names
409
+ as identities. For an alternative-profile stage, its target, name, or logical
410
+ index selects the attempt that claimed the race, including a claimant whose
411
+ payload failed. An exact Slurm ID instead selects that physical job directly,
412
+ so superseded attempts and internal gate, commit, or cleanup jobs remain
413
+ inspectable. Exact Slurm IDs take precedence over logical indexes. Ambiguous
414
+ selectors are rejected with the matching indices.
415
+
416
+ Use `--tail N` to start with the last `N` lines and `--follow` to stream new
417
+ content. Combining them follows from the tailed content; without `--tail`,
418
+ following starts with the complete existing log. If an active job has not
419
+ created its log yet, follow mode waits and reports that once on standard error.
420
+ Following stops after the job becomes terminal and all available bytes have
421
+ been emitted. Submission tracebacks recorded by DAWGZ are printed when no
422
+ Slurm log was created. If an alternative race is unresolved, a normal request
423
+ lists its attempt IDs, profiles, and states. Follow mode waits while an attempt
424
+ can still claim the race and then follows the claimant. When accounting has no
425
+ state for an attempt, follow mode checks whether it remains in the Slurm queue
426
+ before waiting. It does not choose an attempt merely because its log appeared
427
+ first. Detailed status and logs are not supported for local-backend workflow
428
+ records.
429
+
430
+ `klej logs --clear WORKFLOW_ID` removes a known-terminal workflow's complete
431
+ DAWGZ record, including its logs, and removes its rows from `workflows.csv`.
432
+ `klej logs --clear-all` does the same for every known-terminal Slurm and local
433
+ workflow while retaining pending, running, and unknown records. Both commands
434
+ ask for confirmation; `--force` skips that prompt but never permits deletion
435
+ of an active or unknown workflow. For versioned plans, this safety check
436
+ includes every physical attempt and internal gate, commit, and cleanup job,
437
+ including jobs hidden by the logical status view. Cleanup does not cancel
438
+ jobs, release klej locks, or delete DVC outputs.
439
+
440
+ Do not run record cleanup concurrently with workflow submission. DAWGZ does
441
+ not coordinate updates to its `workflows.csv` index with external cleanup.