gflow-cli 0.26.0__tar.gz → 0.27.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/CHANGELOG.md +22 -1
  2. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/PKG-INFO +1 -1
  3. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/INDEX.md +4 -1
  4. gflow_cli-0.27.0/docs/LIVE_VERIFICATION_v0.27.0.md +100 -0
  5. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/MOVIE.md +70 -3
  6. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/PROJECT_STATUS.md +42 -1
  7. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/schemas/movie-handoff.schema.json +9 -0
  8. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/pyproject.toml +1 -1
  9. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/__init__.py +1 -1
  10. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_movie.py +57 -11
  11. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/composition.py +80 -4
  12. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/movie_manifest.py +92 -4
  13. gflow_cli-0.27.0/tests/cli/test_movie_manifest_style_variants.py +455 -0
  14. gflow_cli-0.27.0/tests/composition/test_style_variants.py +272 -0
  15. gflow_cli-0.27.0/tests/composition/test_style_variants_e2e.py +305 -0
  16. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/uv.lock +1 -1
  17. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/README.md +0 -0
  18. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/active.md +0 -0
  19. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/branch-review.md +0 -0
  20. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/changelog.md +0 -0
  21. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/check.md +0 -0
  22. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/doc-review.md +0 -0
  23. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/issue-assessment.md +0 -0
  24. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/issue-resolve.md +0 -0
  25. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/known-issues.md +0 -0
  26. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/next.md +0 -0
  27. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/plan.md +0 -0
  28. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/pr-council-review.md +0 -0
  29. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/predict.md +0 -0
  30. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/release.md +0 -0
  31. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/scenario.md +0 -0
  32. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/sonar.md +0 -0
  33. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.claude/commands/gflow/status.md +0 -0
  34. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.env.template +0 -0
  35. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.gitattributes +0 -0
  36. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/CODEOWNERS +0 -0
  37. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  38. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/copilot-instructions.md +0 -0
  39. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/dependabot.yml +0 -0
  40. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/workflows/ci.yml +0 -0
  41. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/workflows/external-pr-triage.yml +0 -0
  42. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/workflows/governance-advisory.yml +0 -0
  43. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/workflows/governance-benchmark.yml +0 -0
  44. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/workflows/main-base-guard.yml +0 -0
  45. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.github/workflows/release.yml +0 -0
  46. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.gitignore +0 -0
  47. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.gitleaks.toml +0 -0
  48. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.pre-commit-config.yaml +0 -0
  49. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/.secrets.baseline +0 -0
  50. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/AGENTS.md +0 -0
  51. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/CLAUDE.md +0 -0
  52. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/CONFIGURATION.md +0 -0
  53. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/CONTRIBUTING.md +0 -0
  54. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/DISCLAIMER.md +0 -0
  55. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/GEMINI.md +0 -0
  56. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/KNOWN_ISSUES.md +0 -0
  57. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/LICENSE +0 -0
  58. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/PLAN.md +0 -0
  59. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/README.md +0 -0
  60. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/RELEASE.md +0 -0
  61. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/ROADMAP.md +0 -0
  62. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/conftest.py +0 -0
  63. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docker-compose.yml +0 -0
  64. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/AGENT_GUIDE.md +0 -0
  65. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/AGENT_UI_E2E.md +0 -0
  66. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/AGENT_UI_RECON.md +0 -0
  67. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/ARCHITECTURE.md +0 -0
  68. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/AUTHENTICATION.md +0 -0
  69. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/CHARACTER.md +0 -0
  70. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/CHARACTER_RECON.md +0 -0
  71. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/CONFIGURATION.md +0 -0
  72. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/DATA_LAYER.md +0 -0
  73. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/DEBUGGING.md +0 -0
  74. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/DEMOS.md +0 -0
  75. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/DEVELOPMENT.md +0 -0
  76. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/E2E_TESTING.md +0 -0
  77. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/EXTERNAL_STORAGE.md +0 -0
  78. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/GITHUB.md +0 -0
  79. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
  80. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/IMAGE_UPSCALE_RECON.md +0 -0
  81. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
  82. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
  83. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
  84. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
  85. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
  86. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
  87. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
  88. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
  89. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
  90. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.19.0.md +0 -0
  91. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.20.0.md +0 -0
  92. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.20.1.md +0 -0
  93. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.21.0.md +0 -0
  94. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.22.0.md +0 -0
  95. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.23.0.md +0 -0
  96. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.24.0.md +0 -0
  97. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.25.0.md +0 -0
  98. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.26.0.md +0 -0
  99. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
  100. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
  101. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
  102. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
  103. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
  104. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/MCP.md +0 -0
  105. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/PROMPT_EXPANSION.md +0 -0
  106. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/SECURITY.md +0 -0
  107. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/TOOLS.md +0 -0
  108. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/USAGE.md +0 -0
  109. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/USER_GUIDE.md +0 -0
  110. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/assets/demo-split-pf.gif +0 -0
  111. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/assets/example-run.gif +0 -0
  112. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/assets/examples.webp +0 -0
  113. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/medium_tutorial.md +0 -0
  114. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/sonar-cleanup-tracker.md +0 -0
  115. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
  116. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-22-mcp-server/PLAN.md +0 -0
  117. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-22-mcp-server/PREDICT.md +0 -0
  118. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-22-mcp-server/SCENARIO.md +0 -0
  119. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/PLAN.md +0 -0
  120. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/PREDICT.md +0 -0
  121. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/SCENARIO.md +0 -0
  122. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-24-gflow-studio-scaffold/PLAN.md +0 -0
  123. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-24-rest-api-layer/PLAN.md +0 -0
  124. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/plans/2026-06-26-mcp-e2e-test/PLAN.md +0 -0
  125. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/research/2026-06-27-tool-abstraction-evaluation.md +0 -0
  126. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/docs/superpowers/specs/2026-06-29-issue-assessment-workflow-design.md +0 -0
  127. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/README.md +0 -0
  128. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/batch_from_config.py +0 -0
  129. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/multi_prompt_t2i.py +0 -0
  130. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/sample_config.json +0 -0
  131. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/sample_prompts.txt +0 -0
  132. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/single_image_t2i.py +0 -0
  133. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/examples/workflow_chain.py +0 -0
  134. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/llms.txt +0 -0
  135. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/README.md +0 -0
  136. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/01_upload_image.json +0 -0
  137. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
  138. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
  139. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/04_archive_workflow.json +0 -0
  140. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/05_createProject.json +0 -0
  141. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/06_batchGenerateImages.json +0 -0
  142. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
  143. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
  144. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
  145. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
  146. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
  147. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/12_create_scene.json +0 -0
  148. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
  149. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/14_get_scene_workflows.json +0 -0
  150. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
  151. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/ci/check_doc_links.py +0 -0
  152. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/ci/check_materiality.py +0 -0
  153. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/ci/check_repo_hygiene.py +0 -0
  154. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/debug_editor.py +0 -0
  155. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/debug_gen_settings.py +0 -0
  156. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/debug_settings.py +0 -0
  157. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/_recording_client.py +0 -0
  158. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/_spike_common.py +0 -0
  159. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/active_plan.py +0 -0
  160. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/analyze_agent_ui_capture.py +0 -0
  161. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
  162. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
  163. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
  164. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
  165. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
  166. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/capture_locale_invariants.py +0 -0
  167. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
  168. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/character_create_spike.py +0 -0
  169. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/character_create_spike_v2.py +0 -0
  170. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/dump_character_selectors.js +0 -0
  171. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/make_project.py +0 -0
  172. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/materiality_backtest.py +0 -0
  173. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/monitor_pr_38.py +0 -0
  174. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/patch_character.py +0 -0
  175. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/skillopt/README.md +0 -0
  176. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/skillopt/harness.py +0 -0
  177. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/skillopt/tasks.json +0 -0
  178. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_char_editor_dom.py +0 -0
  179. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_char_gen_capture.py +0 -0
  180. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_image_upscale_capture.py +0 -0
  181. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_image_upscale_drive.py +0 -0
  182. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
  183. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
  184. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
  185. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
  186. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_movie_attach_payload.py +0 -0
  187. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
  188. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
  189. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_movie_picker_select.py +0 -0
  190. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_movie_voice_list.py +0 -0
  191. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_patch_entity.py +0 -0
  192. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/dev/spike_patchright.py +0 -0
  193. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/diag/README.md +0 -0
  194. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/diag/capture_flow_traffic.py +0 -0
  195. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/diag/memory_profile.py +0 -0
  196. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/diag/recaptcha_mint.py +0 -0
  197. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/e2e/agentic_image_e2e.ps1 +0 -0
  198. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/e2e/capture_agent_toggle.py +0 -0
  199. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/record_demo.ps1 +0 -0
  200. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/smoke_image.py +0 -0
  201. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/smoke_real_chrome_image.py +0 -0
  202. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/smoke_video_editor.py +0 -0
  203. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/smoke_worker_style.py +0 -0
  204. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/scripts/verify_chrome_auth_viability.py +0 -0
  205. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/README.md +0 -0
  206. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/gflow-cli/SKILL.md +0 -0
  207. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/issue-assessment/SKILL.md +0 -0
  208. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/issue-resolve/SKILL.md +0 -0
  209. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/plan/SKILL.md +0 -0
  210. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/pr-council-review/SKILL.md +0 -0
  211. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/predict/SKILL.md +0 -0
  212. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/scenario/SKILL.md +0 -0
  213. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/skills/status/SKILL.md +0 -0
  214. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/sonar-project.properties +0 -0
  215. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/__main__.py +0 -0
  216. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/_cli_helpers.py +0 -0
  217. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/__init__.py +0 -0
  218. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/_engine.py +0 -0
  219. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/_retry.py +0 -0
  220. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
  221. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/character.py +0 -0
  222. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/client.py +0 -0
  223. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/dto.py +0 -0
  224. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/image.py +0 -0
  225. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/image_upscale.py +0 -0
  226. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/recaptcha.py +0 -0
  227. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/routes.py +0 -0
  228. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/scene.py +0 -0
  229. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/__init__.py +0 -0
  230. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/_common.py +0 -0
  231. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
  232. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/base.py +0 -0
  233. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/drivers/__init__.py +0 -0
  234. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/drivers/agentic.py +0 -0
  235. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/drivers/base.py +0 -0
  236. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/drivers/classic.py +0 -0
  237. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/drivers/factory.py +0 -0
  238. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
  239. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
  240. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
  241. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
  242. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/ui_automation.py +0 -0
  243. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/transports/ui_automation_video.py +0 -0
  244. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/api/video.py +0 -0
  245. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/__init__.py +0 -0
  246. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/base.py +0 -0
  247. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/cookies.py +0 -0
  248. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/factory.py +0 -0
  249. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
  250. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/real_chrome.py +0 -0
  251. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/strategies.py +0 -0
  252. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/auth/verification.py +0 -0
  253. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/browser_manager.py +0 -0
  254. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/chain.py +0 -0
  255. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/chain_manifest.py +0 -0
  256. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli.py +0 -0
  257. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_character.py +0 -0
  258. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_data.py +0 -0
  259. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_image.py +0 -0
  260. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_models.py +0 -0
  261. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_run.py +0 -0
  262. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_scene.py +0 -0
  263. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_tools.py +0 -0
  264. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/cli_video.py +0 -0
  265. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/config.py +0 -0
  266. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/__init__.py +0 -0
  267. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/chain_repo.py +0 -0
  268. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
  269. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
  270. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
  271. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
  272. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
  273. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
  274. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0007_queue.sql +0 -0
  275. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/0008_add_operation_expanded_prompt.sql +0 -0
  276. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
  277. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/models.py +0 -0
  278. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/queries.py +0 -0
  279. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/recorder.py +0 -0
  280. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/redaction.py +0 -0
  281. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/repository.py +0 -0
  282. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/data/store.py +0 -0
  283. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/errors.py +0 -0
  284. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/exceptions.py +0 -0
  285. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/image_batch.py +0 -0
  286. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/json_output.py +0 -0
  287. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/manifest.py +0 -0
  288. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/mcp/__init__.py +0 -0
  289. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/mcp/prompts.py +0 -0
  290. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/mcp/resources.py +0 -0
  291. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/mcp/server.py +0 -0
  292. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/mcp/tools.py +0 -0
  293. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/media.py +0 -0
  294. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/observability.py +0 -0
  295. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/paths.py +0 -0
  296. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/profile_store.py +0 -0
  297. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/services/__init__.py +0 -0
  298. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/services/character_create.py +0 -0
  299. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/storage.py +0 -0
  300. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/__init__.py +0 -0
  301. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/banned.py +0 -0
  302. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/builtin/__init__.py +0 -0
  303. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/builtin/creative-director.toml +0 -0
  304. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/expander.py +0 -0
  305. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/invocation.py +0 -0
  306. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/loader.py +0 -0
  307. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/registry.py +0 -0
  308. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/runtime.py +0 -0
  309. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/tools/spec.py +0 -0
  310. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/ui/app.py +0 -0
  311. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/ui/server.py +0 -0
  312. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/worker/daemon.py +0 -0
  313. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/src/gflow_cli/worker/queue.py +0 -0
  314. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tasks/lessons.md +0 -0
  315. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/test_assets/sample_batch.json +0 -0
  316. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/test_assets/sample_batch.tsv +0 -0
  317. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/test_assets/sample_batch_invalid.tsv +0 -0
  318. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/__init__.py +0 -0
  319. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/__init__.py +0 -0
  320. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/fixtures/character_gen_response.json +0 -0
  321. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/fixtures/patch_entity_response.json +0 -0
  322. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_aisandbox_auth_error.py +0 -0
  323. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
  324. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_bearer_redaction.py +0 -0
  325. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_character.py +0 -0
  326. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client.py +0 -0
  327. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_character.py +0 -0
  328. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_delete_characters.py +0 -0
  329. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_generate_character.py +0 -0
  330. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_image.py +0 -0
  331. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_launch_kwargs.py +0 -0
  332. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_patch_entity.py +0 -0
  333. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_scene.py +0 -0
  334. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_client_upscale.py +0 -0
  335. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_concurrency.py +0 -0
  336. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_dto.py +0 -0
  337. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_engine.py +0 -0
  338. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_image.py +0 -0
  339. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_image_dto.py +0 -0
  340. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_image_upscale.py +0 -0
  341. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
  342. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_recaptcha.py +0 -0
  343. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_retry.py +0 -0
  344. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_routes.py +0 -0
  345. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_routes_character.py +0 -0
  346. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_routes_scene.py +0 -0
  347. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_sapisidhash_helper.py +0 -0
  348. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_scene_models.py +0 -0
  349. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_video.py +0 -0
  350. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/test_video_request.py +0 -0
  351. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/__init__.py +0 -0
  352. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/conftest.py +0 -0
  353. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/drivers/__init__.py +0 -0
  354. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/drivers/test_agentic.py +0 -0
  355. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/drivers/test_factory.py +0 -0
  356. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_base.py +0 -0
  357. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_bearer.py +0 -0
  358. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_common.py +0 -0
  359. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
  360. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_factory.py +0 -0
  361. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_fingerprint.py +0 -0
  362. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_sapisidhash.py +0 -0
  363. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_transport_timeout.py +0 -0
  364. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_ui_automation.py +0 -0
  365. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_ui_automation_batch.py +0 -0
  366. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
  367. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_ui_automation_video.py +0 -0
  368. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/api/transports/test_ui_character_editor.py +0 -0
  369. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/auth/strategies/test_factory.py +0 -0
  370. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/auth/strategies/test_strategies.py +0 -0
  371. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/auth/test_cookies.py +0 -0
  372. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/auth/test_verification.py +0 -0
  373. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/__init__.py +0 -0
  374. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_auth_list.py +0 -0
  375. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_character.py +0 -0
  376. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_character_create.py +0 -0
  377. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_data.py +0 -0
  378. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_image.py +0 -0
  379. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
  380. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_image_selector_drift.py +0 -0
  381. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_image_upscale.py +0 -0
  382. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_models.py +0 -0
  383. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_movie.py +0 -0
  384. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_run.py +0 -0
  385. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_scene.py +0 -0
  386. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_tools.py +0 -0
  387. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_video.py +0 -0
  388. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_cli_video_chain.py +0 -0
  389. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_error_handling.py +0 -0
  390. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_helpers.py +0 -0
  391. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_movie_manifest.py +0 -0
  392. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_settings_validation.py +0 -0
  393. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
  394. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/composition/test_character.py +0 -0
  395. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/composition/test_compose_prompt.py +0 -0
  396. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/composition/test_handoff.py +0 -0
  397. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/conftest.py +0 -0
  398. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/__init__.py +0 -0
  399. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_chain_repo.py +0 -0
  400. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_find_incomplete_character.py +0 -0
  401. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_models.py +0 -0
  402. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_packaging.py +0 -0
  403. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_recorder.py +0 -0
  404. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_recorder_character.py +0 -0
  405. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_redaction.py +0 -0
  406. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_repository.py +0 -0
  407. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_scene_persistence.py +0 -0
  408. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_settings_and_errors.py +0 -0
  409. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/data/test_store_migrations.py +0 -0
  410. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/dev/test_recording_client.py +0 -0
  411. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/__init__.py +0 -0
  412. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/conftest.py +0 -0
  413. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
  414. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
  415. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_chain_e2e.py +0 -0
  416. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_character_create_e2e.py +0 -0
  417. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_daemon_e2e.py +0 -0
  418. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_data_layer_e2e.py +0 -0
  419. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
  420. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_image_batch_e2e.py +0 -0
  421. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
  422. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_json_output_e2e.py +0 -0
  423. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
  424. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_scene_compose_live.py +0 -0
  425. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_tools_e2e.py +0 -0
  426. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_transports_e2e.py +0 -0
  427. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
  428. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
  429. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/__init__.py +0 -0
  430. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/auth.feature +0 -0
  431. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/auth_login.feature +0 -0
  432. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/character_create.feature +0 -0
  433. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/character_read.feature +0 -0
  434. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/conftest.py +0 -0
  435. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/image.feature +0 -0
  436. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/image_upscale.feature +0 -0
  437. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/locale_picker_include.feature +0 -0
  438. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_auth_login_steps.py +0 -0
  439. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_auth_steps.py +0 -0
  440. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_character_create_steps.py +0 -0
  441. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_character_read_steps.py +0 -0
  442. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_image_steps.py +0 -0
  443. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_image_upscale_steps.py +0 -0
  444. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_locale_picker_include_steps.py +0 -0
  445. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_step_collision_guard.py +0 -0
  446. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_video_agent_ui_steps.py +0 -0
  447. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/test_video_chain_steps.py +0 -0
  448. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/video_agent_ui.feature +0 -0
  449. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/features/video_chain.feature +0 -0
  450. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/fixtures/__init__.py +0 -0
  451. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/fixtures/seeded_catalog.py +0 -0
  452. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/image_batch/__init__.py +0 -0
  453. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/image_batch/test_image_manifest.py +0 -0
  454. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/image_batch/test_observability_events.py +0 -0
  455. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/integration/__init__.py +0 -0
  456. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/integration/conftest.py +0 -0
  457. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/integration/constants.py +0 -0
  458. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/integration/test_storage_gcs.py +0 -0
  459. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/integration/test_storage_s3.py +0 -0
  460. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/mcp/__init__.py +0 -0
  461. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/mcp/conftest.py +0 -0
  462. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/mcp/test_server.py +0 -0
  463. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/mcp/test_stdio_transport.py +0 -0
  464. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/mcp/test_tools_helpers.py +0 -0
  465. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/mcp/test_tools_wired.py +0 -0
  466. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/scripts/test_analyze_agent_ui_capture.py +0 -0
  467. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
  468. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/scripts/test_check_materiality.py +0 -0
  469. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
  470. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/scripts/test_materiality_backtest.py +0 -0
  471. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/services/__init__.py +0 -0
  472. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/services/test_character_create_redaction.py +0 -0
  473. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/services/test_character_create_saga.py +0 -0
  474. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
  475. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/smoke/__init__.py +0 -0
  476. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/smoke/test_profile_account_smoke.py +0 -0
  477. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/smoke/test_real_flow.py +0 -0
  478. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_auth.py +0 -0
  479. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_browser_manager.py +0 -0
  480. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_chain.py +0 -0
  481. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_chain_manifest.py +0 -0
  482. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_cli_data.py +0 -0
  483. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_config.py +0 -0
  484. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_conftest_isolation.py +0 -0
  485. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_data_queries.py +0 -0
  486. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_documentation_gate.py +0 -0
  487. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_errors.py +0 -0
  488. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_errors_403.py +0 -0
  489. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_json_output.py +0 -0
  490. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_manifest.py +0 -0
  491. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_marker_registry.py +0 -0
  492. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_media.py +0 -0
  493. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_observability.py +0 -0
  494. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_paths.py +0 -0
  495. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_profile_store.py +0 -0
  496. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/test_smoke.py +0 -0
  497. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/__init__.py +0 -0
  498. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_banned.py +0 -0
  499. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_expander.py +0 -0
  500. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_invocation.py +0 -0
  501. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_loader.py +0 -0
  502. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_registry.py +0 -0
  503. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_runtime.py +0 -0
  504. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/tools/test_spec.py +0 -0
  505. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/ui/test_app.py +0 -0
  506. {gflow_cli-0.26.0 → gflow_cli-0.27.0}/tests/worker/test_daemon.py +0 -0
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.27.0] — 2026-07-07
11
+
12
+ ### Added
13
+
14
+ - **Global `[style]` block with named variants in `movie.toml` (Issue #239):** channel-format
15
+ videos can now express a visual style system (e.g. monochrome → warm color arc) once in the
16
+ `[style]` block and select it per-scene via `style_variant`, without repeating style text in
17
+ every scene's action. Adds `prefix`/`suffix` fields to `[style]`, `[style.variants.*]`
18
+ sub-tables, per-scene `style_variant` and `style_suffix` fields, deterministic composition
19
+ rule, and `style_applied` (variant/prefix/suffix/scene_suffix) per clip in the handoff
20
+ manifest. `none` is a reserved variant name (opt-out keyword) and may not be defined.
21
+ - **Prompt-aware resume for `movie run`:** completed scenes now persist a SHA-256 hash of
22
+ their composed prompt (`style_hash`); on resume, a scene whose prompt changed (style edit,
23
+ variant switch, action tweak) is regenerated instead of silently skipped. Unchanged scenes
24
+ still cost nothing. Old state files fall back to comparing the stored prompt text and are
25
+ never re-run on a guess. Dry-run shows the resolved style per scene and marks stale scenes
26
+ as `re-run (style changed)`.
27
+
10
28
  ## [0.26.0] — 2026-07-06
11
29
 
12
30
  ### Added
@@ -1812,7 +1830,10 @@ shell-script template that branches on these codes.
1812
1830
 
1813
1831
  First skeleton. Not functional end-to-end yet.
1814
1832
 
1815
- [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.24.0...HEAD
1833
+ [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.27.0...HEAD
1834
+ [0.27.0]: https://github.com/ffroliva/gflow-cli/compare/v0.26.0...v0.27.0
1835
+ [0.26.0]: https://github.com/ffroliva/gflow-cli/compare/v0.25.0...v0.26.0
1836
+ [0.25.0]: https://github.com/ffroliva/gflow-cli/compare/v0.24.0...v0.25.0
1816
1837
  [0.24.0]: https://github.com/ffroliva/gflow-cli/compare/v0.23.0...v0.24.0
1817
1838
  [0.23.0]: https://github.com/ffroliva/gflow-cli/compare/v0.22.0...v0.23.0
1818
1839
  [0.22.0]: https://github.com/ffroliva/gflow-cli/compare/v0.21.0...v0.22.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gflow-cli
3
- Version: 0.26.0
3
+ Version: 0.27.0
4
4
  Summary: Unofficial CLI for Google Flow — drive Veo image-to-video generations from the terminal.
5
5
  Project-URL: Homepage, https://github.com/ffroliva/gflow-cli
6
6
  Project-URL: Issues, https://github.com/ffroliva/gflow-cli/issues
@@ -22,7 +22,10 @@ Welcome to the `gflow-cli` documentation. This index is the routing layer: it te
22
22
  | [CONTRIBUTING](../CONTRIBUTING.md) | TDD workflow, test categories, coverage targets | Before opening a PR |
23
23
  | **[docs/DEVELOPMENT.md](DEVELOPMENT.md)** | Branching model, PR protocol, e2e gate, version bump protocol, AI-assisted workflow | Understanding the end-to-end dev process |
24
24
  | **[docs/E2E_TESTING.md](E2E_TESTING.md)** | Layer model, cost sub-markers, run commands, isolation patterns, roadmap to contract/replay layer | Running or extending e2e tests; cost control |
25
+ | **[docs/AGENT_UI_E2E.md](AGENT_UI_E2E.md)** | Deterministic e2e runbook for the Agentic UI image path (force-agent trigger, validation ledger) | Live-verifying Agentic-UI features |
25
26
  | **[docs/GITHUB.md](GITHUB.md)** | Maintainer PR triage protocol, forked PR handling, SonarCloud scenarios | Reviewing or merging a GitHub PR |
27
+ | [docs/sonar-cleanup-tracker.md](sonar-cleanup-tracker.md) | SonarCloud zero-smells cleanup tracker (`chore/sonar-zero-cleanup`) | Resuming or auditing the Sonar cleanup effort |
28
+ | [docs/medium_tutorial.md](medium_tutorial.md) | Long-form tutorial article (Medium draft): Veo + Imagen from the terminal | Writing or updating outreach/tutorial content |
26
29
  | [.env.template](../.env.template) | All environment variables with defaults | Setting up a new shell or container |
27
30
  | **[docs/AUTHENTICATION.md](AUTHENTICATION.md)** | Full auth flow, session storage, multi-account, refresh | First `gflow auth login`, or auth errors |
28
31
  | **[docs/CONFIGURATION.md](CONFIGURATION.md)** | All env vars, precedence chain, default paths per OS | Tuning behaviour, picking output paths |
@@ -117,7 +120,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
117
120
  **"A gflow command hangs / fails — where do I start?"** → [DEBUGGING § Quick reference](DEBUGGING.md#quick-reference)
118
121
  **"Flow's UI broke a selector — how do I diagnose it?"** → [DEBUGGING § Inspecting Flow's live UI](DEBUGGING.md#inspecting-flows-live-ui)
119
122
  **"What does each `ui_automation.*` log event mean?"** → [DEBUGGING § Listener & HTTP-layer debugging](DEBUGGING.md#listener--http-layer-debugging)
120
- **"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [v0.19.0](LIVE_VERIFICATION_v0.19.0.md) · [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
123
+ **"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.27.0](LIVE_VERIFICATION_v0.27.0.md) (**`[style]` block with named variants + prompt-aware resume — credit-free CLI verification**: real `movie run --dry-run` 3-way ledger proving resolved-style display, `skip (done)` on matching `style_hash`, and `re-run (style changed)` after a style edit; composition is local, wire path untouched, so no Veo credits spent — first credited styled run will confirm visually). Prior: [v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [v0.19.0](LIVE_VERIFICATION_v0.19.0.md) · [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
121
124
  **"Where is the reverse-engineered wire protocol for a feature?"** → the `*_RECON.md` design docs: [CHARACTER_RECON](CHARACTER_RECON.md) (Flow character entity protocol), [IMAGE_UPSCALE_RECON](IMAGE_UPSCALE_RECON.md) (`/v1/flow/upsampleImage` wire). Naming convention: one `<FEATURE>_RECON.md` per reverse-engineered surface, kept as the durable spec after the feature ships.
122
125
  **"What was live-verified for the data layer (PR #58)?"** → [LIVE_VERIFICATION_data_layer](LIVE_VERIFICATION_data_layer.md) — 1 Imagen + 1 Veo credit on denon82, 6-layer ledger (file + magic + Pillow + DB rows + CLI round-trip + structlog)
123
126
  **"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
@@ -0,0 +1,100 @@
1
+ # Live Verification — v0.27.0
2
+
3
+ Release date: 2026-07-07. Headline: **global `[style]` block with named variants
4
+ in `movie.toml`** (Issue #239, PR #259) — express a visual style arc once
5
+ (prefix/suffix + `[style.variants.*]`) and select it per-scene via
6
+ `style_variant` / `style_suffix` — plus **prompt-aware resume** for `movie run`
7
+ (completed scenes persist a `style_hash`; a scene whose composed prompt changed
8
+ is regenerated instead of silently skipped).
9
+
10
+ Verification run: 2026-07-07, real CLI (`gflow movie run --dry-run`), Windows,
11
+ `.venv` build of the release tree. **Deliberately credit-free**: this release is
12
+ pure local prompt composition + state logic — the Flow wire path (transports,
13
+ selectors, auth) is untouched, so no live Veo generation was spent. The dry-run
14
+ path exercises the identical manifest→parse→compose→resume-decision pipeline the
15
+ live run uses; only `_generate_scene` (unchanged this release) is skipped.
16
+
17
+ ## Scope
18
+
19
+ | Change | Surface | Verdict |
20
+ |---|---|---|
21
+ | `[style]` prefix/suffix + `[style.variants.*]`, per-scene `style_variant`/`style_suffix` | `composition.py`, `movie_manifest.py` | ✅ CLI dry-run + 91 unit/integration tests |
22
+ | Prompt-aware resume (`style_hash`, `is_stale_for`, re-run on style edit) | `movie_manifest.py`, `cli_movie.py` | ✅ CLI dry-run 3-way ledger (below) |
23
+ | Dry-run shows resolved style + stale marker | `cli_movie.py` | ✅ CLI dry-run output |
24
+ | `style_applied` (variant/prefix/suffix/scene_suffix) per clip + schema | `composition.py`, `docs/schemas/movie-handoff.schema.json` | ✅ Unit-tested (jsonschema validation) |
25
+ | Reserved `none` variant name, strict variant parsing | `movie_manifest.py` | ✅ Unit-tested (ConfigurationError paths) |
26
+
27
+ ## Evidence ledger (credit-free CLI runs)
28
+
29
+ Manifest: 3 scenes — `s1-base` (base style), `s2-warm` (`style_variant="warm"` +
30
+ `style_suffix="golden hour light"`), `s3-optout` (`style_variant="none"`), with
31
+ `prefix = "SCENE:"` and a base suffix.
32
+
33
+ 1. **Composed prompt (canonical order)** — `compose_prompt` for `s1-base`
34
+ produced exactly:
35
+ `SCENE: Walks through a rainy plaza. Wide shot. Black and white cinematic
36
+ street photography, film grain.` — prefix first, sentence-composed
37
+ action/framing, raw base suffix last.
38
+ 2. **Run 1 (fresh state)** — plan shows the resolved style per scene and full
39
+ cost: `s1-base … style=base 1 credit(s)`, `s2-warm … style=warm
40
+ +style_suffix 1 credit(s)`, `s3-optout … style=none 1 credit(s)`,
41
+ `Estimated credits: ~3`.
42
+ 3. **Run 2 (s1 completed in `movie-state.json` with matching `style_hash`)** —
43
+ `s1-base … skip (done)`, `Estimated credits: ~2`. State file JSON carries
44
+ `"style_hash": "<sha256 of the composed prompt>"` (round-tripped via
45
+ `MovieState.save`/`load`).
46
+ 4. **Run 3 (base suffix edited to "Sepia archival newsreel look.")** —
47
+ `s1-base … re-run (style changed) 1 credit(s)`, `Estimated credits: ~3`.
48
+ The stale scene is regenerated; the unchanged `s2-warm`/`s3-optout`
49
+ decisions are unaffected. This is the exact staleness bug the feature
50
+ exists to prevent (stale clip silently skipped after a style edit).
51
+ 5. **User-confirmable artifact** — the three plan outputs above are
52
+ reproducible with any manifest; the same decision logic drives the real run
53
+ loop (`_run_movie` calls the identical `is_stale_for` check before spending
54
+ credits).
55
+
56
+ ### Not live-verified this cycle (recorded, not omitted)
57
+
58
+ Actual Veo generation with a style-suffixed prompt (i.e. confirming Flow's
59
+ model renders the requested grade) was **not** exercised — it costs 20 credits
60
+ per scene and the prompt text demonstrably reaches the existing, unchanged
61
+ generation path (`_run_one_scene` passes the same `compose_prompt` output that
62
+ v0.26.0 passed). First credited `movie run` on a styled manifest will confirm
63
+ visually; no code path is untested locally.
64
+
65
+ ### Pre-existing display nit (not this release)
66
+
67
+ The plan line's `[t2v]`/`[r2v]` mode tag is swallowed by rich markup
68
+ (`console.print` interprets `[t2v]` as a style tag) — present on `develop`
69
+ before PR #259; cosmetic; tracked as a follow-up.
70
+
71
+ ## Pre-tag gates
72
+
73
+ - `/gflow:check`: hygiene ✅ · ruff ✅ (no rewrites) · pyright `0 errors` ✅ ·
74
+ full suite 2054 passed / 7 skipped ✅ (local), 3.11/3.12/3.13 matrix +
75
+ SonarCloud quality gate ✅ on PR #259 (same tree).
76
+ - `/code-review` (independent, 8-angle): 10 findings — all fixed in
77
+ `61a9887` before merge.
78
+ - `/gflow:doc-review`: mechanical pass UPDATED×2 (PROJECT_STATUS current
79
+ release was stale at v0.24.0 — v0.25.0/v0.26.0/v0.27.0 entries added; 3 docs
80
+ added to INDEX), links 23/23 green. Council verdict: **GREEN/YELLOW/YELLOW**
81
+ across 3 auditors — 1 Tier-1 finding (this doc overclaimed "91
82
+ style-variants tests"; actual collection is 59 — fixed in the release-prep
83
+ commit), Tier-2 fixed (stale MOVIE.md "active development" banner), Tier-3
84
+ deferred (hardcoded `build_handoff` `generator.version="0.14.0"`; MOVIE.md
85
+ error-path doc anchor; USAGE.md movie coverage). Council reports at
86
+ `tmp/council/0{1,2,3}-*.md` (local-only).
87
+
88
+ ## Post-tag evidence
89
+
90
+ *(filled after the v0.27.0 tag push / publish)*
91
+
92
+ ## Automated coverage
93
+
94
+ - 59 style-variants tests across `tests/composition/test_style_variants.py`,
95
+ `test_style_variants_e2e.py`, `tests/cli/test_movie_manifest_style_variants.py`
96
+ (composition order, variant resolution, reserved `none`, strict parsing,
97
+ `style_applied` + jsonschema validation, `resume_hash` properties,
98
+ `is_stale_for` all four branches, state round-trip).
99
+ - Full suite green: 2054 passed, 7 skipped (local) and 3.11/3.12/3.13 matrix +
100
+ SonarCloud quality gate green on PR #259. `pyright src` 0 errors, ruff clean.
@@ -5,9 +5,9 @@ clips that share the **same characters** (face + voice) across every scene. It
5
5
  is the orchestration layer on top of `gflow character` (entity creation) and
6
6
  `gflow video` (R2V generation).
7
7
 
8
- > Status: under active development on the movie feature branch. The wire
9
- > protocol and CLI surface below are live-verified but may still change before
10
- > release.
8
+ > Status: shipped (since v0.14.0) and evolving. The CLI surface below is
9
+ > release-stable per SemVer; alpha (`0.x`) minor versions may still extend or
10
+ > adjust it, with changes noted in the CHANGELOG.
11
11
 
12
12
  ## Quick start
13
13
 
@@ -31,6 +31,18 @@ title = "E2E Stickman"
31
31
  project = "6ba50219-…" # reusable Flow project id
32
32
  output_dir = "./out"
33
33
 
34
+ [style]
35
+ look = "cinematic, shallow depth of field"
36
+ palette = "warm golden tones"
37
+ negative = "no text, no watermark"
38
+ suffix = "Black and white cinematic street photography, photorealistic, film grain."
39
+
40
+ [style.variants.warm]
41
+ suffix = "Warm golden-hour cinematic grade."
42
+
43
+ [style.variants.cool]
44
+ suffix = "Cool blue-toned cinematic grade."
45
+
34
46
  [[characters]]
35
47
  name = "Stickman"
36
48
  identity = "entity" # reuse a Flow CHARACTER entity across scenes
@@ -47,6 +59,14 @@ line = "We finally made it to the top!"
47
59
  aspect = "9:16"
48
60
  model = "veo-lite"
49
61
  duration = 8
62
+
63
+ [[scenes]]
64
+ id = "close-up"
65
+ action = "smiles with excitement"
66
+ style_variant = "warm" # selects [style.variants.warm]
67
+ style_suffix = "golden hour light" # appended after the variant suffix
68
+ aspect = "9:16"
69
+ duration = 6
50
70
  ```
51
71
 
52
72
  On first run, characters with `identity = "entity"` are created once (image
@@ -54,6 +74,53 @@ generation — **free**, no credits) and cached. Each scene then generates a cli
54
74
  that **reuses the same Flow CHARACTER entity** so the character drives every
55
75
  scene from one identity.
56
76
 
77
+ ### Style variants
78
+
79
+ The `[style]` block defines a **global visual style** applied to every scene.
80
+ For channel-format videos where the style changes across scenes (e.g. a
81
+ monochrome → warm color arc), use **named variants**:
82
+
83
+ ```toml
84
+ [style]
85
+ prefix = "" # optional, prepended before the composed scene text
86
+ suffix = "Black and white cinematic street photography, photorealistic, film grain."
87
+
88
+ [style.variants.warm]
89
+ suffix = "Warm golden-hour cinematic grade."
90
+
91
+ [style.variants.cool]
92
+ suffix = "Cool blue-toned cinematic grade."
93
+ ```
94
+
95
+ Per-scene selection:
96
+
97
+ | Field | Effect |
98
+ |---|---|
99
+ | `style_variant = "warm"` | Selects `[style.variants.warm]` — its `suffix` replaces the base `suffix`. |
100
+ | `style_variant = "none"` | Opts out of all style suffixes (base + variant). Prefix is still applied. |
101
+ | *(omitted)* | Uses the base `[style]` suffix. |
102
+ | `style_suffix = "sunset light"` | Scene-specific text appended **after** the variant/base suffix. |
103
+
104
+ `none` is a **reserved name**: defining `[style.variants.none]` is a
105
+ configuration error, so the opt-out keyword can never shadow a real variant.
106
+
107
+ **Composition rule** (deterministic, no LLM):
108
+
109
+ ```
110
+ final_prompt = [prefix] + composed_scene_text + [variant.suffix | base.suffix] + [scene.style_suffix]
111
+ ```
112
+
113
+ The applied style is recorded per clip in `<manifest>-handoff.json` as
114
+ `style_applied` (`variant`, `prefix`, `suffix`, `scene_suffix`), so downstream
115
+ tools (Remotion, editors) can introspect it.
116
+
117
+ **Resume:** each completed scene's state records a hash of its composed
118
+ prompt. If you edit the style (or anything else that changes a scene's
119
+ prompt) and re-run, only the affected scenes are regenerated — unchanged
120
+ scenes are still skipped and cost no credits. State files written by older
121
+ gflow-cli versions fall back to comparing the stored prompt text; scenes with
122
+ no stored prompt are never re-run automatically.
123
+
57
124
  > **Consistency is best-effort, not pixel-exact.** gflow guarantees the *right
58
125
  > entity rides the wire* (`consistency_method = entity`); the final on-screen
59
126
  > fidelity is Flow's Veo R2V model, which may reinterpret minimalist or
@@ -4,6 +4,43 @@
4
4
 
5
5
  ## Current release
6
6
 
7
+ **v0.27.0 — alpha.** **Global `[style]` block with named variants + prompt-aware resume
8
+ for `gflow movie` (#239).** A `movie.toml` can now express a visual style system once —
9
+ `prefix`/`suffix` on `[style]` plus `[style.variants.*]` sub-tables — and select it
10
+ per-scene via `style_variant` / `style_suffix` (deterministic composition, `none`
11
+ reserved as the opt-out keyword). The handoff manifest records `style_applied`
12
+ (variant/prefix/suffix/scene_suffix) per clip. Resume is now prompt-aware: completed
13
+ scenes persist a `style_hash`; a scene whose composed prompt changed is regenerated
14
+ instead of silently skipped, and dry-run marks it `re-run (style changed)`. Carries
15
+ forward v0.26.0 (i2i select-in-place by UUID). Verification:
16
+ [LIVE_VERIFICATION_v0.27.0](LIVE_VERIFICATION_v0.27.0.md) (credit-free CLI ledger).
17
+
18
+ **Develop (unreleased, post-v0.27.0):** *(empty — develop is the staging branch for the next release).*
19
+
20
+ <details><summary>v0.26.0 — image i2i references a generated image by UUID (select in place)</summary>
21
+
22
+ **v0.26.0 — alpha.** **Reference a generated image in `image i2i` by its Flow UUID.**
23
+ A `reference_images` entry that is a media UUID is attached by **selecting the
24
+ already-existing asset in Flow's reference picker** (located by UUID in the thumbnail
25
+ URL, surfaced by display-name search when hidden) — no duplicate upload; local upload
26
+ remains the fallback. Generated images also record their Flow `display_name` (credited
27
+ @C1ph3r404). Verification: [LIVE_VERIFICATION_v0.26.0](LIVE_VERIFICATION_v0.26.0.md)
28
+ (live e2e GREEN).
29
+
30
+ </details>
31
+
32
+ <details><summary>v0.25.0 — remote-UUID i2v + silent-failure guards</summary>
33
+
34
+ **v0.25.0 — alpha.** **`video i2v` from a generated image's UUID proven live (#237)** —
35
+ the picker-search attach was reworked to a local-upload path, producing a real 8s
36
+ interpolation from a catalogued UUID. Home-`.env` config matrix (#240) verified live.
37
+ Two silent failures made loud: video-as-image download rejection and rejected-upload
38
+ fail-fast. Verification: [LIVE_VERIFICATION_v0.25.0](LIVE_VERIFICATION_v0.25.0.md).
39
+
40
+ </details>
41
+
42
+ <details><summary>v0.24.0 — `--project` parity across CLI + MCP</summary>
43
+
7
44
  **v0.24.0 — alpha.** **`--project` parity across CLI + MCP.** The video commands
8
45
  (`video t2v`/`i2v`/`r2v`) gain `--project <id>` to generate into an existing Flow project
9
46
  instead of a scratch one (#233/#234), matching `image t2i`/`i2i`; and the MCP
@@ -13,7 +50,7 @@ capability (`payload["project_id"]`) and validate the id identically. Carries fo
13
50
  v0.23.0 (MCP generation live + macOS 401 fix). Verification:
14
51
  [LIVE_VERIFICATION_v0.24.0](LIVE_VERIFICATION_v0.24.0.md).
15
52
 
16
- **Develop (unreleased, post-v0.24.0):** *(empty — develop is the staging branch for the next release).*
53
+ </details>
17
54
 
18
55
  <details><summary>v0.23.0 — MCP generation live + macOS 401 fixed</summary>
19
56
 
@@ -99,6 +136,10 @@ reporter-verified e2e on macOS).
99
136
  | Tools framework: `gflow tools` group + `--tool` + `creative-director` + "My Tools" + MCP parity | ✅ done (v0.22.0) |
100
137
  | MCP generation wired to FlowWorker (tool→queue→download→record) + `tools` applied + i2v/r2v boundary validation | ✅ done (v0.23.0) |
101
138
  | macOS generation 401 fixed — `/fx` cookie-path read + headed-context seed (#222/#230) | ✅ done (v0.23.0) |
139
+ | `--project <id>` on `video t2v/i2v/r2v` + MCP `project` parameter (#233/#234/#235) | ✅ done (v0.24.0) |
140
+ | `video i2v` from a generated image's UUID (#237) + home-`.env` matrix (#240) + silent-failure guards | ✅ done (v0.25.0) |
141
+ | `image i2i` references a generated image by UUID — select in place, no duplicate upload + `display_name` capture | ✅ done (v0.26.0) |
142
+ | `movie.toml` `[style]` block with named variants + prompt-aware resume (`style_hash`) (#239) | ✅ done (v0.27.0) |
102
143
  | `gflow video batch` (TSV manifest) on `ui_automation` | ⏳ Phase B |
103
144
  | Persistence layer (stay-mounted batch sessions across project boundaries) | ⏳ Phase B |
104
145
  | Provider abstraction for official Veo 3.1 API | ⏳ planned |
@@ -50,6 +50,15 @@
50
50
  "dialogue": { "type": "array" },
51
51
  "prompt": { "type": ["string", "null"] },
52
52
  "status": { "enum": ["completed", "failed"] },
53
+ "style_applied": {
54
+ "type": "object",
55
+ "properties": {
56
+ "variant": { "type": ["string", "null"] },
57
+ "prefix": { "type": ["string", "null"] },
58
+ "suffix": { "type": ["string", "null"] },
59
+ "scene_suffix": { "type": ["string", "null"] }
60
+ }
61
+ },
53
62
  "x_gflow": { "type": "object" }
54
63
  }
55
64
  }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gflow-cli"
3
- version = "0.26.0"
3
+ version = "0.27.0"
4
4
  description = "Unofficial CLI for Google Flow — drive Veo image-to-video generations from the terminal."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -1,3 +1,3 @@
1
1
  """gflow-cli — unofficial CLI for Google Flow."""
2
2
 
3
- __version__ = "0.26.0"
3
+ __version__ = "0.27.0"
@@ -35,7 +35,14 @@ from gflow_cli._cli_helpers import _make_provider_dir, _resolve_profile, run_wit
35
35
  from gflow_cli.api.character import CharacterImageRequest
36
36
  from gflow_cli.api.client import FlowApiClient
37
37
  from gflow_cli.api.video import Aspect, GenerateVideoRequest, Mode, VideoModel, VideoStarted
38
- from gflow_cli.composition import Character, Scene, build_handoff, compose_prompt
38
+ from gflow_cli.composition import (
39
+ Character,
40
+ Scene,
41
+ StyleSpec,
42
+ build_handoff,
43
+ compose_prompt,
44
+ resume_hash,
45
+ )
39
46
  from gflow_cli.config import get_settings
40
47
  from gflow_cli.data.recorder import OperationRecorder
41
48
  from gflow_cli.errors import ConfigurationError, DataStoreError, WireFormatError
@@ -68,6 +75,10 @@ output_dir = "./out/my-movie" # optional
68
75
  look = "cinematic, shallow depth of field"
69
76
  palette = "warm golden tones"
70
77
  negative = "no text, no watermark"
78
+ suffix = "Black and white cinematic street photography, photorealistic, film grain."
79
+
80
+ [style.variants.warm]
81
+ suffix = "Warm golden-hour cinematic grade."
71
82
 
72
83
  [[characters]]
73
84
  name = "Alice"
@@ -89,6 +100,7 @@ id = "arrival"
89
100
  action = "walks through a busy futuristic plaza, looking around with wonder"
90
101
  framing = "wide"
91
102
  characters = ["Alice"]
103
+ style_variant = "warm" # select [style.variants.warm]; omit for base style
92
104
  aspect = "16:9"
93
105
  duration = 8
94
106
 
@@ -99,6 +111,7 @@ framing = "close-up"
99
111
  characters = ["Alice"]
100
112
  speaker = "Alice"
101
113
  line = "We made it!"
114
+ style_suffix = "golden hour light" # appended after the variant/base suffix
102
115
  aspect = "16:9"
103
116
  duration = 6
104
117
  model = "veo-quality"
@@ -257,15 +270,33 @@ def _print_header(manifest: MovieManifest, *, out_dir: Path, dry_run: bool) -> N
257
270
  console.print(f" characters: {len(manifest.characters)} scenes: {len(manifest.scenes)}")
258
271
 
259
272
 
260
- def _format_scene_line(s: Scene, done: bool, cost: int) -> str:
273
+ def _style_tag(s: Scene, style: StyleSpec) -> str:
274
+ """Return the resolved-style fragment for the plan line, or ''."""
275
+ bits: list[str] = []
276
+ if s.style_variant:
277
+ bits.append(f"style={s.style_variant}")
278
+ elif style.suffix:
279
+ bits.append("style=base")
280
+ if s.style_suffix:
281
+ bits.append("+style_suffix")
282
+ return (" " + " ".join(bits)) if bits else ""
283
+
284
+
285
+ def _format_scene_line(s: Scene, style: StyleSpec, *, done: bool, stale: bool, cost: int) -> str:
261
286
  """Return the single-line plan summary for one scene."""
262
287
  mode = "r2v" if s.characters else "t2v"
263
288
  refs = f" refs=[{', '.join(s.characters)}]" if s.characters else ""
264
289
  framing_tag = f" {s.framing}" if s.framing else ""
265
290
  model_tag = f" {s.model}" if s.model else ""
266
291
  dur_tag = f" {s.duration}s" if s.duration else ""
267
- status = "[dim]skip (done)[/dim]" if done else f"{cost} credit(s)"
268
- return f" [{mode}] {s.id!r}{framing_tag}{model_tag}{dur_tag}{refs} {status}"
292
+ if done:
293
+ status = "[dim]skip (done)[/dim]"
294
+ elif stale:
295
+ status = f"re-run (style changed) {cost} credit(s)"
296
+ else:
297
+ status = f"{cost} credit(s)"
298
+ style_tag = _style_tag(s, style)
299
+ return f" [{mode}] {s.id!r}{framing_tag}{model_tag}{dur_tag}{refs}{style_tag} {status}"
269
300
 
270
301
 
271
302
  def _print_plan(manifest: MovieManifest, state: MovieState) -> None:
@@ -284,10 +315,14 @@ def _print_plan(manifest: MovieManifest, state: MovieState) -> None:
284
315
  scene_credits = 0
285
316
  for s in manifest.scenes:
286
317
  done_state = state.scenes.get(s.id)
287
- done = done_state is not None and done_state.status == "completed"
318
+ done = False
319
+ stale = False
320
+ if done_state is not None and done_state.status == "completed":
321
+ stale = done_state.is_stale_for(compose_prompt(manifest.style, s, manifest.characters))
322
+ done = not stale
288
323
  cost = 0 if done else 1
289
324
  scene_credits += cost
290
- console.print(_format_scene_line(s, done, cost))
325
+ console.print(_format_scene_line(s, manifest.style, done=done, stale=stale, cost=cost))
291
326
 
292
327
  console.print(f"\n Estimated credits: ~{scene_credits}")
293
328
 
@@ -364,6 +399,7 @@ async def _run_one_scene(
364
399
  status="completed",
365
400
  prompt=prompt,
366
401
  consistency_method="entity" if reference_entities else "text",
402
+ style_hash=resume_hash(prompt),
367
403
  )
368
404
  state.save(state_path)
369
405
  console.print(f" saved: {video_result.local_path}")
@@ -390,6 +426,7 @@ async def _run_one_scene(
390
426
  status="failed",
391
427
  prompt=prompt,
392
428
  consistency_method=failed_method,
429
+ style_hash=resume_hash(prompt),
393
430
  )
394
431
  state.save(state_path)
395
432
  if continue_on_error:
@@ -468,11 +505,20 @@ async def _run_movie(
468
505
  for scene in manifest.scenes:
469
506
  scene_state = state.scenes.get(scene.id)
470
507
  if scene_state is not None and scene_state.status == "completed":
471
- console.print(f" [dim]Scene {scene.id!r} — already generated, skipping.[/dim]")
472
- if scene_state.local_path:
473
- completed_local_paths.append(Path(scene_state.local_path))
474
- generated_any = True
475
- continue
508
+ prompt_now = compose_prompt(manifest.style, scene, manifest.characters)
509
+ if scene_state.is_stale_for(prompt_now):
510
+ console.print(
511
+ f" Scene {scene.id!r} — prompt/style changed since last "
512
+ "run; regenerating."
513
+ )
514
+ else:
515
+ console.print(
516
+ f" [dim]Scene {scene.id!r} — already generated, skipping.[/dim]"
517
+ )
518
+ if scene_state.local_path:
519
+ completed_local_paths.append(Path(scene_state.local_path))
520
+ generated_any = True
521
+ continue
476
522
 
477
523
  console.print(f"\n Generating scene [bold]{scene.id!r}[/bold]…")
478
524
  local_path = await _run_one_scene(
@@ -8,6 +8,7 @@ the reusable seam a future second consumer (e.g. remotion) can import.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import hashlib
11
12
  from collections.abc import Mapping
12
13
  from dataclasses import dataclass, field
13
14
  from pathlib import Path
@@ -16,7 +17,12 @@ from typing import Any, cast
16
17
 
17
18
  @dataclass(frozen=True)
18
19
  class StyleSpec:
19
- """Global guiding prompt — every field optional, reused verbatim per scene."""
20
+ """Global guiding prompt — every field optional, reused verbatim per scene.
21
+
22
+ ``prefix`` / ``suffix`` are raw strings prepended / appended to the composed
23
+ prompt. ``variants`` is a name → suffix mapping that lets a manifest express
24
+ a style arc (e.g. monochrome → warm) without repeating text in every scene.
25
+ """
20
26
 
21
27
  look: str | None = None
22
28
  palette: str | None = None
@@ -25,6 +31,22 @@ class StyleSpec:
25
31
  lighting: str | None = None
26
32
  mood: str | None = None
27
33
  negative: str | None = None
34
+ prefix: str | None = None
35
+ suffix: str | None = None
36
+ variants: Mapping[str, str] = field(default_factory=dict[str, str])
37
+
38
+ def resolve_suffix(self, variant_name: str | None) -> str | None:
39
+ """Return the named variant's suffix, or the base suffix when ``None``.
40
+
41
+ Raises ValueError on an unknown variant name (mirrors
42
+ ``Character.resolve_variant``).
43
+ """
44
+ if variant_name is None:
45
+ return self.suffix
46
+ if variant_name not in self.variants:
47
+ msg = f"unknown style variant {variant_name!r}"
48
+ raise ValueError(msg)
49
+ return self.variants[variant_name]
28
50
 
29
51
 
30
52
  @dataclass(frozen=True)
@@ -100,6 +122,8 @@ class Scene:
100
122
  model: str | None = None
101
123
  aspect: str = "16:9"
102
124
  count: int = 1
125
+ style_variant: str | None = None
126
+ style_suffix: str | None = None
103
127
 
104
128
 
105
129
  def _sentence(text: str) -> str:
@@ -164,6 +188,20 @@ def _framing_camera_block(scene: Scene, style: StyleSpec) -> str:
164
188
  return _sentence(framing_cam) if framing_cam else ""
165
189
 
166
190
 
191
+ def resolve_style_suffix(style: StyleSpec, scene: Scene) -> str | None:
192
+ """Resolve the effective style suffix for a scene.
193
+
194
+ ``scene.style_variant = "none"`` → opt out of base and variant suffixes
195
+ (``scene.style_suffix`` is independent and still applied); the manifest
196
+ parser rejects a variant literally named "none" so the sentinel cannot
197
+ shadow a real variant. Otherwise defer to :meth:`StyleSpec.resolve_suffix`
198
+ (variant suffix, or base ``style.suffix`` when no variant is set).
199
+ """
200
+ if scene.style_variant == "none":
201
+ return None
202
+ return style.resolve_suffix(scene.style_variant)
203
+
204
+
167
205
  def compose_prompt(
168
206
  style: StyleSpec,
169
207
  scene: Scene,
@@ -171,12 +209,17 @@ def compose_prompt(
171
209
  ) -> str:
172
210
  """Assemble the final Veo prompt deterministically (canonical order).
173
211
 
174
- Order: action, subject(+variant), setting, look, palette, lighting,
175
- framing+camera, mood, dialogue, negative. Each slot: scene override ->
176
- global -> omit. `negative` MERGES global+scene.
212
+ Order: [prefix] + action + subject(+variant) + setting + look + palette +
213
+ lighting + framing+camera + mood + dialogue + negative + [suffix].
214
+ Each slot: scene override -> global -> omit. ``negative`` MERGES
215
+ global+scene. ``prefix`` / ``suffix`` wrap the entire output.
177
216
  """
178
217
  parts: list[str] = []
179
218
 
219
+ # 0. PREFIX (raw — wraps the entire prompt)
220
+ if style.prefix:
221
+ parts.append(style.prefix)
222
+
180
223
  # 1. ACTION (required)
181
224
  parts.append(_sentence(scene.action))
182
225
 
@@ -221,9 +264,28 @@ def compose_prompt(
221
264
  if negs:
222
265
  parts.append(f"Avoid: {', '.join(negs)}.")
223
266
 
267
+ # 11. SUFFIX (variant or base — raw)
268
+ suffix = resolve_style_suffix(style, scene)
269
+ if suffix:
270
+ parts.append(suffix)
271
+
272
+ # 12. SCENE STYLE_SUFFIX (raw, always last)
273
+ if scene.style_suffix:
274
+ parts.append(scene.style_suffix)
275
+
224
276
  return " ".join(p for p in parts if p)
225
277
 
226
278
 
279
+ def resume_hash(prompt: str) -> str:
280
+ """Return the full SHA-256 hex digest of *prompt* for resume change detection.
281
+
282
+ Same digest as ``gflow_cli.data.redaction.prompt_fields`` computes for
283
+ observability; duplicated here because this module deliberately imports
284
+ nothing from the rest of the package (pure seam).
285
+ """
286
+ return hashlib.sha256(prompt.encode("utf-8")).hexdigest()
287
+
288
+
227
289
  def _build_handoff_characters(manifest: Any, state: Any) -> list[dict[str, Any]]:
228
290
  """Build the ``characters`` list for the handoff manifest."""
229
291
  chars_out: list[dict[str, Any]] = []
@@ -261,6 +323,19 @@ def _build_handoff_clip(
261
323
  stored = getattr(ss, "prompt", None) if ss else None
262
324
  prompt_val = stored or compose_prompt(manifest.style, scene, manifest.characters)
263
325
 
326
+ # Style applied — the styling baked into this clip's prompt. Resolved from
327
+ # the current manifest; the resume check in cli_movie regenerates a scene
328
+ # whenever its composed prompt changes, so this stays consistent with the
329
+ # stored prompt for every clip that survives a run.
330
+ style_applied: dict[str, Any] = {
331
+ "variant": scene.style_variant,
332
+ "prefix": manifest.style.prefix,
333
+ "suffix": resolve_style_suffix(manifest.style, scene),
334
+ "scene_suffix": scene.style_suffix,
335
+ }
336
+ if all(v is None for v in style_applied.values()):
337
+ style_applied = {}
338
+
264
339
  return {
265
340
  "id": scene.id,
266
341
  "index": index,
@@ -274,6 +349,7 @@ def _build_handoff_clip(
274
349
  ],
275
350
  "prompt": prompt_val,
276
351
  "status": status,
352
+ "style_applied": style_applied,
277
353
  "x_gflow": {
278
354
  k: v
279
355
  for k, v in (