Skip to content

fix(api): file /workflow-runs/from-image into the caller's collection (#238) - #279

Open
kevin9327 wants to merge 1 commit into
lightningpixel:devfrom
kevin9327:fix/workflow-runs-collection
Open

fix(api): file /workflow-runs/from-image into the caller's collection (#238)#279
kevin9327 wants to merge 1 commit into
lightningpixel:devfrom
kevin9327:fix/workflow-runs-collection

Conversation

@kevin9327

@kevin9327kevin9327 commented Aug 21, 2026

Copy link
Copy Markdown

What

POST /workflow-runs/from-image now accepts a collection form field (default "Default") and files the run into it, instead of hardcoding "Default".

Why

This is cause 1 of #238. The legacy /generate/from-image already accepts collection, but the canonical workflow-runs endpoint hardcoded it:

background_tasks.add_task(_run_generation, job_id, image_bytes, full_params, "Default")

So a run driven over the REST/MCP surface always landed in Default/, which the Workspace Library does not index — everything generated headlessly existed on disk but showed as "No workspace assets are indexed yet" in the app. With this change a headless caller can target an indexed collection (e.g. Exports) directly, instead of the reported workaround of mirroring files by hand.

The collection name becomes a directory under the workspace (WORKSPACE_DIR / collection), so it has to be sanitized. Rather than copy the legacy endpoint's inline check, I extracted it into a shared sanitize_collection() and call it from both — a path separator, a drive/wildcard character, or an empty name still falls back to "Default", and the two surfaces can't drift on which characters are allowed.

Scope is deliberately just the API half. The indexer roots and job‑meta records (causes 2 and 3 of #238) are separate and untouched here.

Testing

New api/tests/test_workflow_runs_router.py (6 tests):

  • sanitize_collection keeps a plain name (trimmed), refuses / \ : * ? " < > |, and defaults on empty/blank.
  • create_run_from_image forwards the caller's collection to the run and neutralizes a traversing one (../../etcDefault).
Ran 59 tests in 11.7s
OK (skipped=2)

Full api unittest suite green (the 2 skips are pre-existing). No TypeScript touched.

…lightningpixel#238)
The canonical run endpoint hardcoded the "Default" collection, while the legacy
/generate/from-image accepts a `collection` field. A run driven over REST/MCP therefore
always landed in Default/, which the Workspace Library does not index, so everything
generated headlessly existed on disk but showed as "No workspace assets are indexed yet"
in the app (cause 1 of lightningpixel#238).
Accept the same `collection` field on /workflow-runs/from-image and route the run into it.
The name goes through a shared `sanitize_collection()`, extracted from the legacy endpoint's
inline check rather than copied, so the two surfaces cannot drift on which characters may
name a workspace subfolder: a path separator, a drive/wildcard character, or an empty name
still falls back to "Default", because that string becomes a directory under the workspace.
This is the API half of lightningpixel#238. It lets a headless caller target an indexed collection (e.g.
"Exports") instead of the workaround of mirroring files by hand; the indexer roots and job
meta records (causes 2 and 3) are separate and untouched here.
Tests (api/tests/test_workflow_runs_router.py): the sanitizer keeps a plain name, refuses
path and wildcard characters, and defaults on empty; the endpoint forwards the caller's
collection to the run and neutralizes a traversing one. 6 new tests, full api suite 59 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@kevin9327