TF Save Report
The numbers that outlive the browser tab
- levels
- path
TF Sweep Edit and TF Compare Levels produce exactly the numbers a writeup wants - tokens changed per level, spread across arms - and until this node existed, those numbers lived only as text in a node body you had to select and copy by hand. TF Save Report writes a report string to output/trajectory_forcing/<name>.md, so a run's measurements outlive the browser tab and the ComfyUI session. The pack's own docstring makes the case for it in one line: a number whose run can't be traced is worth much less than it looks.
The text input takes any STRING - wire TF Sweep Edit's report or TF Compare Levels' report into it, and the file gets the full table the node body showed you. name (default report) sets the filename. append is on by default, and it's the input that turns this from a file writer into a lab notebook: each run adds to the end of an existing file under a new timestamped heading, so one session's arms accumulate into one comparable table rather than scattering into dozens of files. Turn append off and it writes name-001.md, -002.md, ... instead, when you want each run's report isolated.
The optional levels input is the provenance half and it's the reason to bother wiring this properly. Attach the trajectory and the file opens with the class, seed, and full edit history above the report - a table nobody can trace back to the run that produced it is exactly the "worth much less than it looks" case the node was built to prevent. Combined with TFSaveLevels (the .npz) and TFSaveImages (the .png), one run leaves behind three sibling files that reconstruct the whole experiment: the object, the pictures, and the numbers, all carrying the same provenance.
The path output tells you where the file landed, which is worth reading at least once because the reports accumulate in output/trajectory_forcing/ and it's easy to forget they're there. A folder full of timestamped .md files, each self-describing, is the kind of artifact you thank yourself for weeks later when someone asks "how did you pick that strength?" - and the file answers.
Where people get caught, it's usually scope confusion: this node formats nothing. Whatever string you feed it is what gets written (plus the provenance header when you wire levels in), so it's a persistence node for reports the other TF nodes already generated, not a report generator of its own. Feed it a bare text string and you'll get a bare text file - that's fine, it accepts any STRING, but the sweep and compare tables are what it exists for. And remember append mode accumulates by design, so don't be surprised when the same name keeps growing - that's the feature, and turning it off is the way to get one clean file per run.
Install
Pack-standard: Manager → search Trajectory Forcing, or git clone https://github.com/korayulusan/ComfyUI-TrajectoryForcing into custom_nodes/. Pure file IO like its save siblings, but the pack still runs JAX inside ComfyUI and wants its own Python 3.11 environment on CUDA 12 - requirements.txt is empty on purpose, install.py adds the JAX stack or declines with a reason, and README's env/setup.sh is the fallback. When in doubt:
cd ComfyUI/custom_nodes/ComfyUI-TrajectoryForcing
python -m tf_nodes.doctor
Before you trust a sweep's number enough to write it down, run the workflow again with the same seed - a report is only as reproducible as the trajectory it came from.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | The report. Any STRING output works; the sweep and compare tables are what this exists for. | |
| name | STRING | report | — |
| append | BOOLEAN | true | Add to the end of an existing file under a new timestamped heading, so one session's runs accumulate into one comparable table. Off writes <name>-001.md, -002.md, ... instead. |
| levelsopt | TF_LEVELS | Optional provenance: class, seed and edit history are written above the report so the numbers can be traced back to the run. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| path | STRING | — |