feat(lora-worker): Phase 2 publish-step — copy succeeded LoRA into ComfyUI loras + published_lora_name

On a train reaching succeeded, IN ADDITION to output/{name}.safetensors
(unchanged download source), COPY it into ComfyUI's loras search path at
/storetank/arbo/models/loras/trained/{train_id}/{name}.safetensors and
return published_lora_name (the ComfyUI-relative LoraLoader string) in the
terminal GET /train/{id} payload (arbo Phase 2 auto-registration, §4.1/§7).

- Copy not move; a publish failure NEVER fails the train (keeps succeeded,
  omits published_lora_name, logs the reason to the tailable run log).
- INV-T7-safe: a copy to a fixed computed path, no new free-form args.
- train_id derived from the handoff layout (output_dir.parent.name).
- Provisions loras/trained/ (arbotrain 2775, group-write per the Phase-1
  lesson; world-readable/traversable for ComfyUI) via the deploy playbook.
- ComfyUI verified to resolve nested loras subfolders (no flat fallback).
- Pure path helper unit-tested; 16 tests green.
This commit is contained in:
vh
2026-07-07 00:22:20 -07:00
parent f5c628c56d
commit 74dbfafdf1
5 changed files with 81 additions and 2 deletions
@@ -8,7 +8,7 @@ Pure functions, no GPU, no subprocess — runnable anywhere.
import pytest
from worker import config
from worker.invocation import InvalidTrainRequest, build_command
from worker.invocation import InvalidTrainRequest, build_command, published_relative_path
def _req(**over):
@@ -100,6 +100,13 @@ def test_base_model_outside_allowed_roots_rejected():
build_command(_req(base_model_path="/home/someone/evil.safetensors"))
def test_published_relative_path():
# Phase 2 publish: derive {train_id} from the handoff output_dir -> ComfyUI-relative loras path
assert published_relative_path(
"/worktank/arbo/train/392cf898ac03/output", "sindra_lora"
) == "trained/392cf898ac03/sindra_lora.safetensors"
def test_storetank_checkpoint_allowed():
# the canonical SDXL store (2026-06-13 move) — arbo dispatches base_model_path from here
argv, _, _ = build_command(