Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 5 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
- [ ] `cd ui && npm run build`
- [ ] `git diff --check`
- [ ] E2E/smoke checks: <!-- command + result, or N/A with reason -->
- [ ] Validation scope: fast / `--full` / CI / real media <!-- pick one; do not claim real media from a simulation -->

## Code quality

Expand All @@ -84,9 +85,10 @@

## CI and review

- CI: pending
- Cursor/Bugbot: pending
- Human review: pending
- CI of this HEAD: pending
- Cursor/Bugbot of this HEAD: pending
- Independent agent review of this HEAD: pending
- Human merge click (operational, not code review): pending

<!-- Record the final state here. Do not call the PR ready while required CI or
review is still running. Preserve all findings and their resolutions. -->
Expand Down
43 changes: 41 additions & 2 deletions app/_launch_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -31722,6 +31722,25 @@ def cancelled() -> bool:
)


def _reserve_story_music_submission(body: dict, workspace: str):
"""Persist command/task/candidate IDs before the MiniMax worker starts."""
from services.music_submission import (
MusicSubmissionConflict,
MusicSubmissionError,
submit_music_generation,
)

try:
return submit_music_generation(
workspace_dir=_workspace_dir(workspace),
request={**body, "output_folder": workspace, "workspace": workspace},
)
except MusicSubmissionConflict as exc:
raise HTTPException(status_code=409, detail=str(exc)) from exc
except MusicSubmissionError as exc:
raise HTTPException(status_code=exc.status_code, detail=str(exc)) from exc


@api.post("/api/v1/stories/music-candidates/jobs", status_code=202)
def start_story_music_candidates_job(body: dict):
"""Start observable MiniMax Music generation and return immediately."""
Expand Down Expand Up @@ -31763,8 +31782,17 @@ def start_story_music_candidates_job(body: dict):
detail="Upload a valid reference song before generating a cover",
)

job_id = f"minimax-music-{uuid.uuid4().hex[:12]}"
task_id = f"task-minimax-music-{job_id}"
reserved = _reserve_story_music_submission(body, workspace) or {}
if reserved.get("replay"):
existing = _load_minimax_music_job(str(reserved.get("job_id") or ""))
if existing:
public = _public_minimax_music_job(existing)
public["replay"] = True
return public
# Reservation survived but the MiniMax job did not: start the worker
# again with the reserved IDs instead of returning a dead 202.
job_id = str(reserved.get("job_id") or f"minimax-music-{uuid.uuid4().hex[:12]}")
task_id = str(reserved.get("task_id") or f"task-minimax-music-{job_id}")
now = time.time()
children = []
for index in range(count):
Expand Down Expand Up @@ -31824,8 +31852,19 @@ def start_story_music_candidates_job(body: dict):
"model": model,
"reference_audio_path": reference_audio_path,
},
"generationId": reserved.get("generation_id"),
"commandId": reserved.get("command_id"),
"candidateId": reserved.get("candidate_id"),
"idempotencyKey": reserved.get("idempotency_key"),
}
with _minimax_music_jobs_lock:
existing_live = _minimax_music_jobs.get(job_id)
if existing_live is not None:
# Same reserved job_id is already in flight (concurrent replay
# missed the checkpoint). Do not start a second worker.
public = _public_minimax_music_job(existing_live)
public["replay"] = True
return public

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Publish wipes reserved task identity

Medium Severity

submit_music_generation stores generation_id, candidate_id, command_id, and idempotency_key on the TaskRegistry row, then start_story_music_candidates_job publishes the MiniMax job to the same taskId. That publish upserts a replacement metadata object from job provenance (usually empty) and overwrites workflow/title, so the reserved identity does not survive on the canonical task.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b1c2510. Configure here.

_minimax_music_jobs[job_id] = job
_persist_minimax_music_job(job)
_publish_minimax_music_job(job)
Expand Down
Loading
Loading