Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ff19f78
chore: open 0.9.0-alpha.1 on develop
dkackman Oct 3, 2026
8912bff
docs(releasing): 0.8.0 notes name the hub_error fix, as the published…
dkackman Oct 3, 2026
0571e3a
feat: add new IC-LoRA models for various video editing tasks
dkackman Oct 3, 2026
da4b209
feat(loras): catalog entries for MiniMax-H3, Qwen-Image-2.1 and Z-Ima…
dkackman Oct 3, 2026
fb3da11
docs(plugin): point the H3 and LTX skills at list_loras
dkackman Oct 3, 2026
7298fe4
refactor(plugin): split request-specific detail into skill references
dkackman Oct 3, 2026
80a31f2
fix(plan): #589 #590 - re-price other_device figures for for_each cou…
dkackman Oct 5, 2026
40bd6c6
fix(catalog): #590 - measured mps cost entries for ltx2 text-to-video…
dkackman Oct 5, 2026
03346fe
fix(plan): #593 - shifted per-entry fields in a list driver reset to …
dkackman Oct 5, 2026
c5a2979
test(plan): #593 - give the list-driver entries a prompt field
dkackman Oct 5, 2026
876ba98
fix(plan): #593 - a numeric string in a list-driver entry compares as…
dkackman Oct 5, 2026
6fa1a8d
fix(templates): #594 - assemble-and-score threads sample_rate into th…
dkackman Oct 5, 2026
0e36715
fix(joins): #594 - mixed-rate shots resampled to a pinned sample_rate…
dkackman Oct 5, 2026
e3fbcfe
feat(exports): #595 - export_job reports the zip's real gating; skill…
dkackman Oct 5, 2026
69a3ac8
Merge feat/592-a-export-gating: #595
dkackman Oct 5, 2026
7f473b1
docs(proposals): #592 - close-out record for export_job bulk download
dkackman Oct 5, 2026
2112bbc
Merge docs/592-close-out: #592 design record
dkackman Oct 5, 2026
dd74e17
style: ruff format test_plugin_skills
dkackman Oct 5, 2026
5d50934
docs(releasing): draft 0.9.0 notes
dkackman Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .claude/skills/model-family-onboarding/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ triggering; call `get_server_info` and a shape-filtered `list_workflows`
before trusting any name; the shape decision as choices; the hard numeric
rules; the vendor pointer for prompts and nothing else; validate, quote cost,
run, look, and the family's failure modes; sources with dates. Near the size
of the README it derives from, under the 12 KB cap.
of the README it derives from, `SKILL.md` under the 12 KB cap. What only some
requests need (a long vendor spec, a multi-shot recipe, checkpoint swaps) goes
in `references/<topic>.md` beside it, named by path at the point an agent
needs it; the tests read references as part of the skill and fail on a
reference no `SKILL.md` links.

Add the family's numbers to `tests/test_plugin_skills.py`, each checked
against the diffusers module that enforces it, and any quoted vendor text to
Expand Down
2 changes: 1 addition & 1 deletion docs/MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ references written in the same session.
| `run_workflow(workflow_path=None, inline_workflow=None, arguments=None, acknowledged_cost=False, workspace=None, wait_seconds=0)` | exactly one of `workflow_path` (a catalog name from `list_workflows`, with or without `.json`, or a path to a workflow file on the server) or `inline_workflow`, optional `arguments`, `acknowledged_cost`, `workspace`, `wait_seconds` | Queue a workflow for generation. Returns as soon as the job is queued - unless `wait_seconds` is above 0, in which case the call then waits on the queued job exactly as `wait_for_job(job_id, timeout_seconds=wait_seconds)` would (same per-call cap, clamped not honoured) and the result carries the queued-job fields plus that wait's (`status`, `still_running`, `waited_seconds`, `timeout_requested_seconds`, `timeout_applied_seconds`, `timeout_capped`, the slim `job`); when the cap covers the job's runtime one call is the run and the wait, and a `still_running: true` result is followed with `wait_for_job` as before. `workspace` names the workspace for this one call without switching the session to it - use it to pin a job whose `output:` or `asset:` references live in a workspace other than the session's - `acknowledged_cost` is `true` or the bound `{fingerprint, minutes, downloads}` from the validate plan; a 409 means the plan changed and the message carries the new estimate, and nothing is waited on |
| `get_job(job_id)` | `job_id` | Get a job's status, warnings, output manifest, error and traceback; each manifest entry's `subfolder` is the in-run subfolder the step declared - by convention `final` for the deliverable, `intermediate` for scratch, `''` for none. A running job also carries `progress` (below) |
| `get_job_workflow(job_id)` | `job_id` | The REST equivalent is `GET /api/jobs/{id}/workflow` (see [SERVER.md](SERVER.md#jobs-api)). The workflow the job actually ran. `realized: true` means every mutable input is pinned (arguments, seed, prompts, `output:latest`); `false` means the job predates run tracking and this is the definition as submitted. Pass it to `save_workflow` to keep it under a name |
| `export_job(job_id, overwrite=False)` | `job_id`, `overwrite` | Gather one finished job into `<workspace>/exports/<job id>/` on the server: the realized workflow, the run's manifest, the job row, a README, and copies of the assets, earlier-run inputs and outputs. Returns the directory, a zip URL, the file list with sizes and the total. The three JSON files are in the zip, not repeated here - get_job_workflow and get_job serve them individually. **The directory is on the machine running the server**, like `download_output`'s destination. `auth_required` says whether opening the zip needs this server's bearer token, which this agent cannot attach to someone else's fetch (#353): when false, fetch `open_url` yourself and unpack it into `exports/` under the session's working directory (a deliverable, not a temp file) - the archive already unpacks into one folder named after the job id, so do not create that folder first; when true, hand `open_url` to the person instead of fetching it |
| `export_job(job_id, overwrite=False)` | `job_id`, `overwrite` | Gather one finished job into `<workspace>/exports/<job id>/` on the server: the realized workflow, the run's manifest, the job row, a README, and copies of the assets, earlier-run inputs and outputs. Returns the directory, a zip URL, the file list with sizes and the total. The three JSON files are in the zip, not repeated here - get_job_workflow and get_job serve them individually. **The directory is on the machine running the server**, like `download_output`'s destination. The zip needs no token (`/exports/*.zip` is ungated like `/outputs`, #592), so `auth_required` is false and the agent fetches `open_url` itself (prefixing a relative one with the server address) and unpacks it into `exports/` under the session's working directory (a deliverable, not a temp file) - the archive already unpacks into one folder named after the job id, so do not create that folder first; only an agent without HTTP gives the user `open_url`. `true` is kept as a forward guard for a gated zip, when `open_url` is handed to the person instead |
| `get_job_events(job_id, after=-1, limit=200)` | `job_id`, `after`, `limit` | Get a page of a job's progress events |
| `wait_for_job(job_id, timeout_seconds=20)` | `job_id`, `timeout_seconds` | Block until a job reaches a terminal status, or `timeout_seconds` elapses. **One call blocks for at most the server's cap** — 55 seconds unless the deployment sets `DW_MCP_MAX_WAIT_SECONDS` higher, and the tool's description states the live value; a larger `timeout_seconds` is clamped, not honoured. Ask for the job's `plan.estimate` plus a margin: under the cap that is one call for the whole job, over it one call per cap. A deployment raises the cap only as far as its clients (and anything between them and the server) hold one silent HTTP request open - Claude Code's limit is `MCP_TOOL_TIMEOUT`. Every reply carries `waited_seconds`, `timeout_requested_seconds`, `timeout_applied_seconds` and `timeout_capped`, so a capped return is distinguishable from an elapsed one. Use instead of hand-polling `get_job`/`get_job_events` in a loop; if it returns `still_running: true`, call it again. Returns a slim job - status, warnings, error, `run_id` and `run_version` (the run's `v5`, as the gallery labels it), and the manifest once finished - without the arguments; `get_job` has those. A running job also carries `progress` (below) |
| `cancel_job(job_id)` | `job_id` | Ask a queued or running job to stop |
Expand Down
60 changes: 60 additions & 0 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,63 @@ notes from commits at tag time (see below). This section is a scratch pad
for items a branch's author wants the next release note to name; clear it
when a release ships.

### 0.9.0

<!-- Drafted from v0.8.0..dd74e17b (develop). Paste into the GitHub release body once the tag has published: gh release edit v0.9.0 --notes-file ... -->

A smaller range than 0.8.0: the LoRA catalog grows to cover three more
bases, the cost planner is corrected for list-driven and `for_each` steps,
and `export_job` reports its zip's real auth gating. No upgrade-order
changes.

**LoRA catalog** (docs/LORAS.md)

- 11 LTX-2.5 entries added: IC-LoRAs for alpha generation, clean plate,
colorization, day-to-night, layout-to-render, detail refinement,
restoration, SDR-to-HDR and water simulation, plus cinemagraph and
slow-motion control.
- MiniMax-H3 goes to 13 trial and 3 rejected entries beyond the 0.8.0 set:
styles, speech, orbits, action and motion, and a diffusers-native 4-step
turbo. FastVideo FastH3 (`.diff` keys), RAVEN (unrecognised prefix, loads
nothing silently) and TaoMate (lowercase `lora_a`/`lora_b`) are rejected.
- New bases: Qwen-Image-2.1 (10 trial, including three few-step distills
that need their scheduler and sigma overrides, plus edit LoRAs; Fun-Acc
rejected) and Z-Image-Turbo (13 trial). The Z-Image entries match
`Tongyi-MAI/Z-Image-Turbo` only; whether they apply to the SDNQ
checkpoint is untested.
- Every new entry pins the repo's current sha, and its header was read
against the installed diffusers converter.

**Cost planning**

- `other_device` figures are re-priced for `for_each` counts and shifted
list drivers (#589, #590).
- A shifted per-entry field in a list driver resets to unknown, and a
summed child figure takes the children's basis (#593). A numeric string
in a list-driver entry compares as its number (#593).
- Measured MPS cost entries for `templates/ltx2/text-to-video` and Music 3
(#590).

**Fixes**

- `assemble-and-score` threads `sample_rate` into its edit join, and mixed-rate
shots resampled to a pinned `sample_rate` no longer draw a warning advising
you to pass it (#594).
- `export_job` reports the zip's real gating: `/exports/*.zip` is ungated
like `/outputs`, so `auth_required` is false whether or not the server has
a token. The MCP `next` text and the skills now say to fetch `open_url` and
unpack into `exports/` to bring a project home (#595, #592).

**Plugin**

- The `ltx-2.5`, `minimax-h3` and `minimax-music3` skills moved
request-specific detail (the LTX caption spec, H3 checkpoint and LoRA
combinations and cuts recipes, Music 3 loudness) into `references/`
beside each `SKILL.md`, which are read when the skill points there. The
12 KiB cap applies to `SKILL.md` alone; a new test fails on an unlinked
reference or a dead link.
- The H3 and LTX skills point at `list_loras`.

### 0.8.0

<!-- Drafted from v0.7.0..a9e1e72b (develop). Paste into the GitHub release body once the tag has published: gh release edit v0.8.0 --notes-file ... -->
Expand Down Expand Up @@ -38,6 +95,9 @@ upscaler and two security fixes.
- GHSA-crqf-hw9p-r739: `create_workspace`'s MCP result is `name`,
`default`, `current` and `next` only; `list_workspaces(detail=true)` is
the opt-in for folder paths. `POST /api/workspaces` is unchanged.
- `GET /api/loras/recommend` and `recommend_loras`: `hub_error` names the
exception type, or the HTTP status, never the exception's text, which
could name the server's HF cache directory. The log keeps the full error.
- UI lockfile bumps for open Dependabot alerts (devalue, dompurify,
brace-expansion, undici).

Expand Down
5 changes: 5 additions & 0 deletions docs/REMOTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ responses add an `absolute_url` / `absolute_zip_url` built from it; nothing
guesses this from request headers, so an unconfigured server omits the
field rather than composing a wrong origin.

The export zip, like `/outputs` and `/inputs` files, needs no token (#592), so
an agent with HTTP fetches `export_job`'s `open_url` itself, prefixing a
relative one with the address it reaches the server at; `DW_PUBLIC_URL` is for
the URLs handed to a person.

## Browser

Open `http://<box>:8765`. Click the key icon next to the theme toggle,
Expand Down
128 changes: 128 additions & 0 deletions docs/proposals/complete/job-export-bulk-download-complete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Bulk download of a job's outputs: export_job reports the zip's real gating (#592)

Written by model `claude-opus-5-5` via provider `anthropic` (close-out,
2026-10-05). Plan v2 was approved by Don on 2026-10-05 (Q1 and Q2 kept
their defaults). Stage 1, #595, shipped the same day at `develop` @
`69a3ac8a` and was verified on its first pass.

## The idea

A field report from an agent job on mini-ai: bringing 41 shots and 7 music
cues back to the Mac meant reading each job's file list and curling every
file. The issue asked for a "fetch everything from this job" call, and
whether it should extend `export_job` or be a new tool, and how large
binary transfer should work over MCP.

## Verdict

**Build smaller: one stage, no new tool.** The bulk download already
existed. `POST /api/jobs/{id}/export` builds `exports/<id>/` (outputs,
inputs, assets, workflow, manifest, job record, README) and returns
`zip_url` (`/exports/<id>.zip`), and `/exports` is ungated by design, beside
`/outputs` (`dw/server/routes/files.py`, pinned in
`tests/test_security_auth.py`, stated in `docs/SERVER.md`).

What broke it was one field. The export response set
`auth_required = bool(state.api_token)`: whether the *server* has a token,
not whether the *zip URL* needs one. `dw_mcp/exports.py` then told the agent
"do not fetch it; hand open_url to the person". On any token-bearing server
(mini-ai, lem) an agent was told not to fetch a URL that needs no token,
which left the per-file curl loop the report describes. #353 had introduced
the flag on the premise that the zip sat behind the token; the code said it
didn't.

**Binary transfer over MCP itself: don't build.** A tool result lands in
the agent's context, so base64 video would swamp it (#203 capped inline
upload at 4 MB for the same reason), and MCP has no channel to the client's
disk (`download_output` over a mounted endpoint writes on the server).
Over MCP, big files travel by URL; the tool's job is to hand over one URL
that works.

## Decisions (Don, 2026-10-05)

- **Q1. Keep `/exports/*.zip` ungated?** Yes. It serves nothing `/outputs`
doesn't already serve ungated. Don required the test pinning "token
configured → `auth_required` false → unauthenticated GET returns 200",
so the field can't drift from the route again.
- **Q2. Keep `auth_required` (always false today)?** Yes: no shape change,
and it guards a future gating of `/exports`.
- **Q3. Surface removal:** none found.

## What was built (stage 1, #595)

- **Server** (`dw/server/routes/jobs.py`): the export response sets
`auth_required = False`, with a comment tying it to `files.py`'s ungated
list. Response shape unchanged, so the OpenAPI dump didn't move.
- **MCP `next`** (`dw_mcp/exports.py`): when `auth_required` is false,
`next` says to fetch `open_url` with whatever HTTP you have (it needs no
token; prefix a relative URL with the server address), and unpack into
`exports/` under the working directory without pre-creating the job-id
folder. Only an agent without HTTP is told to give the user `open_url`.
The `true` branch is unchanged, as the forward guard.
- **MCP description** (`dw_mcp/tools_jobs.py`): "do NOT fetch it" is gone.
It shrank from 1429 to 1313 characters, so `SURFACE_BUDGET` was left
alone.
- **Skills:** `script-to-video` and `series-episodes` end their delivery
sections with "take the project home": `export_job` once per job, then
fetch each zip into `exports/`. `minimax-music3/references/keeping-a-run.md`
lost its stale "can't attach the token" wording. `minimax-h3` already
keyed on the field; `minimax-music3/SKILL.md` and `ltx-2.5` had no export
wording.
- **Docs:** the `export_job` row in `docs/MCP.md`; a paragraph in
`docs/REMOTE.md` saying the zip needs no token and an agent with HTTP
fetches it itself.
- **Tests:**
- `tests/test_server_exports.py::…test_with_a_token_the_zip_is_not_auth_required_and_opens_without_one`:
with a token, `auth_required` is false and a token-less GET of
`zip_url` returns 200 with `<id>/manifest.json` inside; a token-less
export POST is still 401 (Don's Q1).
- `tests/test_mcp_exports.py`: both `next` branches.
- `tests/test_mcp_server.py::test_export_job_description_says_to_fetch_the_ungated_zip`
replaces `…_is_auth_aware`.

### Deviations from the plan

- **No plugin version bump.** `plugins/dw/README.md` ties the plugin's
version to the engine's, bumped only by the release script. The tester's
plugin tree follows `origin/develop`, so the skill text was live without
one.
- The no-HTTP fallback reads "give the user open_url to open" rather than
the old "hand open_url to the person".

## Verified

On lem (token set) at `69a3ac8a`, cases C-F180–C-F182: `export_job` of a
finished job returns `auth_required: false` and the fetch `next`; unknown
id, running *and queued* jobs, and a repeat without `overwrite` are all
refused as before; `overwrite=true` replaces the export identically; the
served description and skills no longer steer away from fetching. The
token-less GET of the zip was not checked over MCP (the tester has no HTTP
client); it rests on the pinned unit test above.

Left behind: no MCP tool reaches `exports/`, so each run of C-F180/C-F181
leaves an `exports/<id>/` on the test server.

## Bounces per stage

- **Stage 1 (#595): 0.** The architecture review passed and the tester
verified on the first pass.

No `usage:` figures were recorded on the stage, so cost is left out (the
plan estimated about $3–4).

## Deferred

- **A multi-job bundle** (`export_jobs([...])` or a series zip): a new tool,
a new naming scheme under `exports/`, a UI flow and new symlink cases,
about 4 stages and pressure on the surface budget. **Bring it back** when
a field report shows a project spread over enough jobs that one zip per
job is the friction; `script-to-video`'s one-job-per-shot film is the
likely trigger.
- **An outputs-only zip** (no `assets/` and `inputs/` copies, so no doubled
server disk): `POST /api/gallery/archive` does name-based zips but is
gated, has no job filter and no MCP tool. **Bring it back** on a report
of server disk pressure from `exports/`.
- **Removing an export over MCP:** no tool reaches `exports/` today, so
exports accumulate on the server. Not in scope; worth a fix if the
outputs-only zip above comes back, or if the test server's `exports/`
grows.
7 changes: 7 additions & 0 deletions docs/proposals/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ remaining deferred fix is recorded in
soft in the faces. Record, including what was deferred (the refine pass,
persisted latents, task weights in `downloads_required`):
`complete/h3-latent-upscale-complete.md`.
- **Bulk download of a job's outputs** (#592, stage #595), shipped
2026-10-05 with no new tool: `export_job` now reports the ungated
`/exports` zip as `auth_required: false` on a token server and tells the
agent to fetch it, and the multi-job skills name it as how a project goes
home. Record, including what was deferred (a multi-job bundle, an
outputs-only zip, removing an export over MCP):
`complete/job-export-bulk-download-complete.md`.

## Declined

Expand Down
Loading
Loading