Skip to content

Commit adf4989

Browse files
ryan-williamsclaude
andcommitted
Preserve \after:\ field when executor rewrites .dvc files
The executor's \`write_dvc_file\` calls were not passing \`artifact.computation.after\`, silently dropping ordering constraints when .dvc files were rewritten after execution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fdf8818 commit adf4989

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Bug: `after:` field dropped when DVX rewrites `.dvc` files
2+
3+
## Problem
4+
5+
When DVX runs a stage and rewrites its `.dvc` file (to update dep hashes, `last_run`, etc.), the `after:` ordering field is silently dropped.
6+
7+
## Root cause
8+
9+
`write_dvc_file` in `src/dvx/run/dvc_files.py:484` accepts an `after` parameter, but the call sites in the executor (`src/dvx/run/executor.py:476` and `:530`) don't pass it:
10+
11+
```python
12+
dvc_file = write_dvc_file(
13+
output_path=Path(path),
14+
cmd=cmd if self.config.provenance else None,
15+
deps=deps_hashes if self.config.provenance else None,
16+
git_deps=git_deps_hashes if self.config.provenance else None,
17+
fetch_schedule=fetch_schedule,
18+
fetch_last_run=fetch_last_run,
19+
# after= is missing!
20+
)
21+
```
22+
23+
The `after` info is available on `artifact.computation.after` (populated from the original `.dvc` file), but never passed through.
24+
25+
## Fix
26+
27+
At both `write_dvc_file` call sites in `executor.py`, pass `after=artifact.computation.after`.
28+
29+
## Repro
30+
31+
Any `.dvc` file with an `after:` block will lose it after `dvx run`:
32+
33+
```yaml
34+
# before
35+
meta:
36+
computation:
37+
cmd: some_command
38+
after:
39+
- other/stage.dvc
40+
41+
# after dvx run
42+
meta:
43+
computation:
44+
cmd: some_command
45+
# after: gone
46+
```

src/dvx/run/executor.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,9 @@ def _execute_artifact(self, artifact: Artifact) -> ExecutionResult:
463463
from datetime import datetime, timezone
464464
fetch_last_run = datetime.now(timezone.utc).isoformat()
465465

466+
# Preserve after: ordering constraints across rewrites
467+
after = artifact.computation.after if artifact.computation else []
468+
466469
if is_side_effect:
467470
# Side-effect: update dep hashes in .dvc, no output hash
468471
dvc_file = None
@@ -480,6 +483,7 @@ def _execute_artifact(self, artifact: Artifact) -> ExecutionResult:
480483
git_deps=git_deps_hashes if self.config.provenance else None,
481484
fetch_schedule=fetch_schedule,
482485
fetch_last_run=fetch_last_run,
486+
after=after or None,
483487
)
484488
if self.config.verbose:
485489
self._log(f" → {dvc_file}")
@@ -536,6 +540,7 @@ def _execute_artifact(self, artifact: Artifact) -> ExecutionResult:
536540
git_deps=git_deps_hashes if self.config.provenance else None,
537541
fetch_schedule=fetch_schedule,
538542
fetch_last_run=fetch_last_run,
543+
after=after or None,
539544
)
540545
if self.config.verbose:
541546
self._log(f" → {dvc_file}")
@@ -587,13 +592,15 @@ def _handle_co_output(self, artifact: Artifact, cmd: str) -> ExecutionResult:
587592
deps_hashes = artifact.computation.get_dep_hashes()
588593
git_deps_hashes = artifact.computation.get_git_dep_hashes()
589594

595+
co_after = artifact.computation.after if artifact.computation else []
590596
dvc_file = write_dvc_file(
591597
output_path=out,
592598
md5=md5,
593599
size=size,
594600
cmd=cmd if self.config.provenance else None,
595601
deps=deps_hashes if self.config.provenance else None,
596602
git_deps=git_deps_hashes if self.config.provenance else None,
603+
after=co_after or None,
597604
)
598605

599606
self._log(f" ✓ {path}: co-output ready")

0 commit comments

Comments
 (0)