feat: always-on UUID log directories with symlink#3074
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0fbac0b. Configure here.
|
|
||
| def inference(config: InferenceConfig): | ||
| config.output_dir.mkdir(parents=True, exist_ok=True) | ||
| setup_log_dir(config.output_dir) |
There was a problem hiding this comment.
Nested inference rotates log symlink
High Severity
Local RL runs call setup_log_dir on the run output_dir, then spawn an inference subprocess whose entrypoint calls setup_log_dir again with default resuming=False. Because inference.toml omits output_dir, the child defaults to outputs, matching the RL default, so the logs symlink is repointed to a new UUID run and component logs land in different runs/ directories.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 0fbac0b. Configure here.
| if tmp_link.exists() or tmp_link.is_symlink(): | ||
| tmp_link.unlink() | ||
| os.symlink(run_dir, tmp_link) | ||
| os.replace(tmp_link, logs_link) |
There was a problem hiding this comment.
Setup fails on legacy logs folder
Medium Severity
setup_log_dir installs the symlink via os.replace onto output_dir/logs. If logs already exists as a regular directory from a prior release or manual layout, replacement raises on Unix instead of migrating or reusing it, so startup fails even when validate_output_dir allows the run.
Reviewed by Cursor Bugbot for commit 0fbac0b. Configure here.
Logs are now always written to output_dir/runs/<uuid>/ with a output_dir/logs symlink pointing to the current run's directory. This isolates logs per run while preserving backward compatibility through the symlink — all existing code that reads get_log_dir(output_dir) / trainer.log continues to work unchanged. When resuming (ckpt.resume_step is set), the existing log directory is reused so logs append to the same run. On clean_output_dir, the entire output_dir is wiped (existing behavior), so a fresh UUID is generated. No config changes needed — the behavior is transparent. SLURM templates also work as-is since /logs resolves through the symlink created by the launcher before sbatch.
0fbac0b to
f0bbeae
Compare


What
Logs are now always written to
output_dir/runs/<uuid>/withoutput_dir/logssymlinked to the current run's directory. This isolates logs per run while preserving full backward compatibility — all existing code that readsget_log_dir(output_dir) / "trainer.log"continues to work unchanged through the symlink.Why
SLURM retrials and re-runs were clobbering logs from previous attempts. With UUID-isolated run directories, historical logs are preserved automatically without any config changes or user opt-in.
How
setup_log_dir()inpathing.pycreatesruns/<uuid>/and atomically symlinkslogsto itckpt.resume_stepset), the existing symlinked directory is reusedclean_output_dir, the entireoutput_diris wiped (existing behavior), so a fresh UUID is generatedFiles changed
src/prime_rl/utils/pathing.py— newsetup_log_dir()functionsrc/prime_rl/entrypoints/rl.py— callsetup_log_dir()inrl()src/prime_rl/entrypoints/sft.py— callsetup_log_dir()insft(), fix inlineoutput_dir / "logs"to useget_log_dir()src/prime_rl/entrypoints/inference.py— callsetup_log_dir()ininference()tests/unit/utils/test_pathing.py— 6 new tests for symlink creation, writability, UUID isolation, and resume reuseTest plan
uv run python -m pytest tests/unit/utils/test_pathing.py -v # 13 passed (7 existing + 6 new)Note
Low Risk
Logging layout only; backward-compatible symlink preserves existing log paths with no auth, checkpoint, or training logic changes.
Overview
Training and inference launches now isolate logs per run under
output_dir/runs/<uuid>/, whileoutput_dir/logsstays a symlink to the active run so existingget_log_dir()paths keep working.A new
setup_log_dir()inpathing.pycreates the UUID directory and atomically repoints thelogssymlink;rl,sft, andinferencecall it afteroutput_diris created. Resume (resuming=True) reuses the symlink target so continued runs append to the same log tree; each fresh start gets a new UUID and priorruns/*dirs are left intact. SFT local torchrun--log-dirnow goes throughget_log_dir()instead of a hardcodedoutput_dir / "logs".Unit tests cover symlink creation, writes through the link, new UUID per non-resume call, and resume reuse.
Reviewed by Cursor Bugbot for commit f0bbeae. Bugbot is set up for automated code reviews on this repo. Configure here.