textflowkit 0.1.8__tar.gz → 0.1.10__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 (201) hide show
  1. {textflowkit-0.1.8 → textflowkit-0.1.10}/.github/workflows/ci.yml +6 -2
  2. {textflowkit-0.1.8 → textflowkit-0.1.10}/CHANGELOG.md +123 -0
  3. {textflowkit-0.1.8 → textflowkit-0.1.10}/Dockerfile +6 -4
  4. {textflowkit-0.1.8 → textflowkit-0.1.10}/PKG-INFO +193 -21
  5. {textflowkit-0.1.8 → textflowkit-0.1.10}/README.md +189 -19
  6. {textflowkit-0.1.8 → textflowkit-0.1.10}/compose.yaml +4 -3
  7. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/adapters.md +95 -12
  8. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/architecture.md +69 -8
  9. textflowkit-0.1.10/docs/assets/architecture-overview.svg +213 -0
  10. textflowkit-0.1.10/docs/assets/local-ui-flow.svg +155 -0
  11. textflowkit-0.1.10/docs/assets/whistle-resume.svg +164 -0
  12. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/index.html +55 -9
  13. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/install.md +114 -27
  14. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/release-checklist.md +35 -8
  15. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/roadmap.md +42 -1
  16. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/site-deployment.md +10 -2
  17. textflowkit-0.1.10/docs/user-manual.md +470 -0
  18. {textflowkit-0.1.8 → textflowkit-0.1.10}/pyproject.toml +20 -5
  19. textflowkit-0.1.10/scripts/smoke_installed_wheel.py +436 -0
  20. {textflowkit-0.1.8 → textflowkit-0.1.10}/scripts/smoke_live_youtube.py +28 -9
  21. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/__init__.py +1 -1
  22. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/adapters/http_server.py +7 -5
  23. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/adapters/mcp_server.py +29 -19
  24. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/cli.py +158 -39
  25. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/checkpoint.py +226 -23
  26. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/engine.py +201 -62
  27. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/pipeline.py +149 -14
  28. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/runner.py +3 -2
  29. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/submission.py +73 -6
  30. textflowkit-0.1.10/src/textflowkit/core/whistle.py +1198 -0
  31. textflowkit-0.1.10/src/textflowkit/core/whistle_assets.py +505 -0
  32. textflowkit-0.1.10/src/textflowkit/ui/__init__.py +13 -0
  33. textflowkit-0.1.10/src/textflowkit/ui/app.py +579 -0
  34. textflowkit-0.1.10/src/textflowkit/ui/capabilities.py +195 -0
  35. textflowkit-0.1.10/src/textflowkit/ui/launcher.py +686 -0
  36. textflowkit-0.1.10/src/textflowkit/ui/ownership.py +602 -0
  37. textflowkit-0.1.10/src/textflowkit/ui/paths.py +179 -0
  38. textflowkit-0.1.10/src/textflowkit/ui/security.py +200 -0
  39. textflowkit-0.1.10/src/textflowkit/ui/static/app.css +285 -0
  40. textflowkit-0.1.10/src/textflowkit/ui/static/app.js +756 -0
  41. textflowkit-0.1.10/src/textflowkit/ui/static/index.html +226 -0
  42. textflowkit-0.1.10/src/textflowkit/ui/uploads.py +247 -0
  43. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_adapters.py +9 -5
  44. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_admission_ownership.py +2 -2
  45. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_atomic_resume.py +9 -9
  46. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_atomic_resume_aba.py +3 -3
  47. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_batch.py +5 -2
  48. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_batch_item_boundary.py +2 -2
  49. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_batch_resume_shared.py +3 -2
  50. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_checkpoint.py +77 -5
  51. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_checkpoint_single_copy.py +9 -3
  52. textflowkit-0.1.10/tests/test_cli.py +228 -0
  53. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cli_owning_recovery.py +5 -5
  54. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cli_stage_feedback.py +2 -2
  55. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_diarize.py +3 -3
  56. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_docx_publication_identity.py +2 -1
  57. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_done_output_integrity.py +6 -2
  58. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_empty_formats_resume.py +3 -2
  59. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_engine_adapters.py +14 -14
  60. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_engine_faster.py +20 -10
  61. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_landing_commands.py +28 -11
  62. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_partial_output_resume.py +3 -0
  63. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_partial_pdf_resume.py +1 -0
  64. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_pipeline_resume_setup.py +5 -1
  65. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_release_boundaries.py +10 -7
  66. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_stage_reuse_resume.py +1 -0
  67. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_submission.py +7 -2
  68. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_submission_validation.py +13 -7
  69. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_translate.py +3 -3
  70. textflowkit-0.1.10/tests/test_ui_assets_and_capabilities.py +294 -0
  71. textflowkit-0.1.10/tests/test_ui_consoleless_logging.py +88 -0
  72. textflowkit-0.1.10/tests/test_ui_existing_routes_unchanged.py +86 -0
  73. textflowkit-0.1.10/tests/test_ui_launcher.py +224 -0
  74. textflowkit-0.1.10/tests/test_ui_ownership_and_shutdown.py +477 -0
  75. textflowkit-0.1.10/tests/test_ui_packaging.py +153 -0
  76. textflowkit-0.1.10/tests/test_ui_real_serve.py +88 -0
  77. textflowkit-0.1.10/tests/test_ui_security.py +168 -0
  78. textflowkit-0.1.10/tests/test_ui_submission_and_download.py +260 -0
  79. textflowkit-0.1.10/tests/test_ui_uploads.py +201 -0
  80. textflowkit-0.1.10/tests/test_whistle_assets.py +490 -0
  81. textflowkit-0.1.10/tests/test_whistle_defaults.py +661 -0
  82. textflowkit-0.1.10/tests/test_whistle_engine.py +1027 -0
  83. textflowkit-0.1.10/tests/test_whistle_job_resume.py +620 -0
  84. textflowkit-0.1.8/docs/user-manual.md +0 -290
  85. textflowkit-0.1.8/scripts/smoke_installed_wheel.py +0 -219
  86. textflowkit-0.1.8/tests/test_cli.py +0 -105
  87. {textflowkit-0.1.8 → textflowkit-0.1.10}/.dockerignore +0 -0
  88. {textflowkit-0.1.8 → textflowkit-0.1.10}/.env.example +0 -0
  89. {textflowkit-0.1.8 → textflowkit-0.1.10}/.gitattributes +0 -0
  90. {textflowkit-0.1.8 → textflowkit-0.1.10}/.github/dependabot.yml +0 -0
  91. {textflowkit-0.1.8 → textflowkit-0.1.10}/.github/workflows/publish-pypi.yml +0 -0
  92. {textflowkit-0.1.8 → textflowkit-0.1.10}/.gitignore +0 -0
  93. {textflowkit-0.1.8 → textflowkit-0.1.10}/.pre-commit-config.yaml +0 -0
  94. {textflowkit-0.1.8 → textflowkit-0.1.10}/CONTRIBUTING.md +0 -0
  95. {textflowkit-0.1.8 → textflowkit-0.1.10}/LEGAL.md +0 -0
  96. {textflowkit-0.1.8 → textflowkit-0.1.10}/LICENSE +0 -0
  97. {textflowkit-0.1.8 → textflowkit-0.1.10}/SECURITY.md +0 -0
  98. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/.nojekyll +0 -0
  99. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/404.html +0 -0
  100. {textflowkit-0.1.8 → textflowkit-0.1.10}/docs/sources.md +0 -0
  101. {textflowkit-0.1.8 → textflowkit-0.1.10}/scripts/fetch_published_fonts.py +0 -0
  102. {textflowkit-0.1.8 → textflowkit-0.1.10}/scripts/verify_readme_release.py +0 -0
  103. {textflowkit-0.1.8 → textflowkit-0.1.10}/scripts/verify_release_ci.py +0 -0
  104. {textflowkit-0.1.8 → textflowkit-0.1.10}/scripts/verify_release_versions.py +0 -0
  105. {textflowkit-0.1.8 → textflowkit-0.1.10}/scripts/write_release_manifest.py +0 -0
  106. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/adapters/__init__.py +0 -0
  107. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/assets/README.md +0 -0
  108. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/assets/selftest-speech.wav +0 -0
  109. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/__init__.py +0 -0
  110. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/batch.py +0 -0
  111. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/bind.py +0 -0
  112. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/cancel.py +0 -0
  113. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/diarize.py +0 -0
  114. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/executor.py +0 -0
  115. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/jobs.py +0 -0
  116. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/model.py +0 -0
  117. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/paths.py +0 -0
  118. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/retrieval.py +0 -0
  119. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/service.py +0 -0
  120. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/sqlite_store.py +0 -0
  121. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/startup.py +0 -0
  122. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/timeutil.py +0 -0
  123. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/core/translate.py +0 -0
  124. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/__init__.py +0 -0
  125. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/_cue_layout.py +0 -0
  126. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/_cue_text.py +0 -0
  127. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/_rename_excl.py +0 -0
  128. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/_rename_noreplace.py +0 -0
  129. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/docx.py +0 -0
  130. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/markdown.py +0 -0
  131. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/pdf.py +0 -0
  132. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/srt.py +0 -0
  133. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/txt.py +0 -0
  134. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/render/vtt.py +0 -0
  135. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/sources/__init__.py +0 -0
  136. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/sources/acquire.py +0 -0
  137. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/sources/detect.py +0 -0
  138. {textflowkit-0.1.8 → textflowkit-0.1.10}/src/textflowkit/sources/scratch.py +0 -0
  139. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/__init__.py +0 -0
  140. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/ollama_stub.py +0 -0
  141. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_acquire.py +0 -0
  142. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_bind.py +0 -0
  143. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cancellation_finalization.py +0 -0
  144. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cli_batch_feedback.py +0 -0
  145. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cli_resume_batch.py +0 -0
  146. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cli_stdout_format.py +0 -0
  147. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_confined_decoder_boundary.py +0 -0
  148. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_confined_indirect_media.py +0 -0
  149. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_confined_resume_boundary.py +0 -0
  150. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_container_example.py +0 -0
  151. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_cue_layout.py +0 -0
  152. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_diarize_example_flags.py +0 -0
  153. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_diarize_token_docs.py +0 -0
  154. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_engine_cache.py +0 -0
  155. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_engine_quiet.py +0 -0
  156. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_executor.py +0 -0
  157. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_executor_store_failure.py +0 -0
  158. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_export.py +0 -0
  159. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_export_file_mode.py +0 -0
  160. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_export_preflight.py +0 -0
  161. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_faster_whisper_docs.py +0 -0
  162. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_fetch_published_fonts.py +0 -0
  163. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_font_notices.py +0 -0
  164. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_http_host_boundary.py +0 -0
  165. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_http_trusted_proxy.py +0 -0
  166. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_job_progress.py +0 -0
  167. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_jobs.py +0 -0
  168. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_landing_page.py +0 -0
  169. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_legal_summary.py +0 -0
  170. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_linux_no_hardlink.py +0 -0
  171. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_live_smoke_script.py +0 -0
  172. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_macos_no_hardlink.py +0 -0
  173. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_mcp_batch_cookie_docs.py +0 -0
  174. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_mcp_http_boundary.py +0 -0
  175. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_mcp_protocol_cleanliness.py +0 -0
  176. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_mcp_terminal_guidance.py +0 -0
  177. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_mcp_tool_descriptions.py +0 -0
  178. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_mcp_work_root.py +0 -0
  179. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_model.py +0 -0
  180. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_paths.py +0 -0
  181. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_pipeline_integrity.py +0 -0
  182. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_production_browser_cookies.py +0 -0
  183. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_publish_ci_gate.py +0 -0
  184. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_publish_fonts_wiring.py +0 -0
  185. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_publish_manifest_wiring.py +0 -0
  186. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_publish_workflow_gate.py +0 -0
  187. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_readme_layout.py +0 -0
  188. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_readme_release_guard.py +0 -0
  189. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_release_manifest.py +0 -0
  190. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_release_surfaces.py +0 -0
  191. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_render.py +0 -0
  192. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_retrieval.py +0 -0
  193. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_sdist_allowlist.py +0 -0
  194. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_service_profile.py +0 -0
  195. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_site_not_found.py +0 -0
  196. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_sources.py +0 -0
  197. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_startup_recovery.py +0 -0
  198. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_stdio_protocol.py +0 -0
  199. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_translate_completeness.py +0 -0
  200. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_verify_release_versions.py +0 -0
  201. {textflowkit-0.1.8 → textflowkit-0.1.10}/tests/test_windows_no_hardlink.py +0 -0
@@ -58,7 +58,11 @@ jobs:
58
58
  run: |
59
59
  python -m pip install --upgrade pip
60
60
  python -m pip install ./packages/textflowkit-fonts
61
- python -m pip install -e ".[dev,mcp,http,export]"
61
+ # `whisper` is installed here because the existing engine tests
62
+ # (openai-whisper's verbose/quiet contract, model-name validation)
63
+ # genuinely need the openai-whisper package present. It is an optional
64
+ # extra now that Whistle is the default engine, so it must be named.
65
+ python -m pip install -e ".[dev,mcp,http,export,whisper]"
62
66
 
63
67
  - name: Show ffmpeg version
64
68
  shell: bash
@@ -86,6 +90,6 @@ jobs:
86
90
  python -m pip install "setuptools>=83" "build>=1.2"
87
91
  python -m build --wheel
88
92
  python -m build --wheel --outdir dist packages/textflowkit-fonts
89
- - name: Smoke all three installed entry points
93
+ - name: Smoke all four installed entry points (CLI, MCP, HTTP, served UI)
90
94
  shell: bash
91
95
  run: python scripts/smoke_installed_wheel.py dist/textflowkit-*.whl dist/textflowkit_fonts-*.whl
@@ -1,5 +1,128 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.1.10 — 2026-10-06
4
+
5
+ **The local browser interface ships.** The loopback-only browser workspace
6
+ (`textflowkit-ui`) is now part of the core package rather than source-only, so a
7
+ regular install of this release carries the command. Core version: 0.1.10;
8
+ unchanged optional fonts: 0.1.6.
9
+
10
+ ### Added
11
+
12
+ - **Local browser interface in the core package.** `textflowkit-ui` serves a
13
+ point-and-click workspace on **this machine only**: it binds `127.0.0.1` (never
14
+ off the machine, whatever `TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a
15
+ browser once serving. It mounts the existing developer HTTP app under `/api`
16
+ **unchanged** — the same routes, submission contract, and job store — and adds
17
+ only transport: a loopback session layer, a streamed upload, a fixed-format
18
+ download, in-workspace playback, and a capability endpoint. It adds **no new
19
+ dependency** beyond the existing `http` extra (FastAPI + uvicorn).
20
+ - **Durable jobs, cancel and resume in the workspace.** Jobs and checkpoints are
21
+ stored durably under the per-user data directory unless
22
+ `TEXTFLOWKIT_DB` / `TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT` is set.
23
+ A dropped file is streamed and capped at **2 GiB** by default
24
+ (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it). A running job can be
25
+ cancelled, and a durable job resumed.
26
+ - **Windows desktop shortcut.** `textflowkit-ui --create-shortcut` creates a Start
27
+ Menu entry that launches with `pythonw` (no console) and needs no `pywin32`.
28
+ Under `pythonw` a startup failure is logged under the per-user data directory
29
+ and shown in a message box rather than failing silently.
30
+ - **Controlled shutdown that drains.** **Stop server** asks this process's own
31
+ uvicorn server to exit through its supported `should_exit` flag; the normal
32
+ lifespan teardown **waits for current jobs to finish** before draining the
33
+ worker pool rather than interrupting them, so a running job's saved progress
34
+ survives and is resumable. It never sends a signal to a pid.
35
+
36
+ ### Changed
37
+
38
+ - **Documentation surfaces.** README, user manual, install notes, developer
39
+ manual, architecture guide, roadmap, release checklist, site deployment, the
40
+ landing page, and the checked-in SVG diagrams now describe the local browser
41
+ interface as a shipped feature and a fifth door, and remove the earlier
42
+ "unreleased candidate / install from source only" wording.
43
+ - **No endpoint-breaking change.** The CLI, Python API, MCP server, and JSON HTTP
44
+ adapter are unchanged in this release; the optional engines, diarization, and
45
+ translation keep their documented dependency requirements.
46
+
47
+ ### Verification boundary
48
+
49
+ Automated coverage runs the UI's routes, packaging, ownership/shutdown, and
50
+ security boundaries. A **native Windows** machine, in a real browser, proved the
51
+ workspace end to end (drop a file, submit, download, stop). That is one platform's
52
+ browser proof, **not** a live Linux or macOS UI run — those platforms are covered
53
+ by automated tests only, and cross-platform CI (Windows/Linux/macOS) is a separate
54
+ step run by the coordinator. **No live optional-backend (translation or
55
+ diarization) run through the UI is claimed.** The engine and packaging changes of
56
+ v0.1.9 are unchanged by this release.
57
+
58
+ ## v0.1.9 — 2026-10-05
59
+
60
+ **Whistle is now the default engine.** This release ships the Whistle integration
61
+ prepared on the `feat/whistle-primary` branch.
62
+
63
+ ### Changed
64
+
65
+ - **Whistle is the default engine; openai-whisper becomes an explicit opt-in.**
66
+ Whistle is a CPU-only native CLI that needs no PyTorch, so a fresh
67
+ `pip install textflowkit` no longer pulls the torch stack. `openai-whisper`
68
+ moves to a new optional `whisper` extra and is selected with `--engine whisper`
69
+ (or `engine="whisper"` on the Python/MCP/HTTP surfaces); the `all` extra still
70
+ includes it. A saved request or checkpoint from before this change keeps running
71
+ on the engine it names, so legacy Whisper work is never silently migrated, and
72
+ legacy engine aliases are preserved so older decoders keep working.
73
+ - **Engine selection is never silent.** An unknown engine name, an unsupported
74
+ language, or a non-CPU device for Whistle is refused at request construction —
75
+ before any media is fetched — and the message names `--engine whisper` as the
76
+ GPU alternative. The engine is not switched to satisfy a request.
77
+
78
+ ### Added
79
+
80
+ - **Whistle engine.** Seven advertised languages (`en`, `de`, `fr`, `es`, `it`,
81
+ `nl`, `pl`); native Windows x86-64/arm64, Linux x86-64/arm64, and Apple Silicon
82
+ (an Intel Mac is refused and pointed at the explicit Whisper engine); no WSL.
83
+ One pinned native binary and one pinned model are downloaded and hash-verified
84
+ on first use, outside the package. Long audio is split into 26-second cores with
85
+ up to 2 seconds of context (each clip ≤ 30 s); a word is owned by the clip whose
86
+ core contains its midpoint, so repeated phrases are preserved. Native
87
+ `--audio-stream` is never used.
88
+ - **Durable per-block resume for Whistle.** A long run persists a partial
89
+ checkpoint after each core; a resume validates the source and decoded-WAV
90
+ hashes and the configuration, then re-runs only the unfinished blocks and adopts
91
+ the rest. A partial body is Whistle-only. Reuses the existing single SQLite
92
+ store and its one-owning-process model.
93
+ - **Prompt, bounded cancellation.** A running Whistle run polls for cancellation
94
+ between clips and terminates its exact owned child process, bounded by a
95
+ per-clip timeout and a bounded stdout cap. A single long `openai-whisper` model
96
+ call is still cancellable only at its next stage boundary.
97
+ - **Telemetry off, unconditionally.** Every Whistle child process forces
98
+ `NEEDLE_TELEMETRY=0`, `DO_NOT_TRACK=1`, and `CI=1`, overriding a parent that
99
+ opted in, and there is no opt-in setting. The product adds no analytics, usage
100
+ SDK, anonymous ids, or events. `TEXTFLOWKIT_OFFLINE` refuses downloads and
101
+ `TEXTFLOWKIT_MODELS_DIR` chooses the asset directory; the pinned first-use
102
+ asset/model download is not telemetry.
103
+
104
+ ### Documentation
105
+
106
+ - README, user manual, install/adapter/architecture/roadmap/release docs and the
107
+ website source now describe Whistle as the shipped default. ROCm (and the
108
+ torch-pinning caveat) is scoped to the explicit `whisper` engine and
109
+ diarization, never to Whistle.
110
+ - Self-contained checked-in SVG architecture drawings were added under
111
+ `docs/assets` and embedded in the README, both manuals (`user-manual.md` and the
112
+ renamed **Developer and integration manual**, `adapters.md`), the architecture
113
+ guide, and the landing page.
114
+ - The live YouTube release smoke pins `--engine whisper --model tiny` so its
115
+ measured default-clip word stays reproducible across the default-engine change.
116
+
117
+ ### Verification boundary
118
+
119
+ Local evidence only: a 4-hour (14,407-second) CPU run produced 555
120
+ clips / 34,596 words / 2,875 segments in 731 seconds, offline with telemetry
121
+ forced off, and a separate two-process run proved durable block resume. These are
122
+ first local test numbers on one machine — not a universal performance claim, and
123
+ not a claim that Whistle's accuracy equals Whisper's. No human-scored
124
+ word-error rate is claimed.
125
+
3
126
  ## v0.1.8 — 2026-10-02
4
127
 
5
128
  The 2026-10-02 audit-lite repair release. Core version: 0.1.8; unchanged optional fonts: 0.1.6. It carries the four fixes for the post-release audit-lite of 2026-10-01 (findings AL-001 – AL-004). Release artifacts and publication status are recorded on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8). Local source-candidate verification does not establish installed-package or individual harness compatibility.
@@ -77,11 +77,13 @@ RUN set -eux; \
77
77
  WORKDIR /app
78
78
  COPY pyproject.toml README.md LICENSE ./
79
79
  COPY src ./src
80
- # The base dependencies are yt-dlp and openai-whisper, so this image is large:
81
- # openai-whisper pulls a torch build. That is the product's own requirement, not
82
- # something this file adds.
80
+ # The base dependencies are yt-dlp alone: the default engine (Whistle) is a
81
+ # native CPU binary fetched on first use, so this image does not pull a torch
82
+ # build. Selecting the openai-whisper engine needs the `whisper` extra named
83
+ # here; the image installs it because the container is meant to serve the
84
+ # Whisper stack too, and openai-whisper is what pulls the torch build.
83
85
  RUN python -m pip install --no-cache-dir --upgrade pip \
84
- && python -m pip install --no-cache-dir ".[http]"
86
+ && python -m pip install --no-cache-dir ".[http,whisper]"
85
87
 
86
88
  # Non-secret defaults only. The roots are the directories created above; Compose
87
89
  # repeats them so the volumes it mounts land on the paths the service reads.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: textflowkit
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Cross-platform media transcription toolkit: URL or file in, timestamped transcripts and subtitle files out.
5
5
  Project-URL: Homepage, https://www.textflowkit.org/
6
6
  Project-URL: Issues, https://github.com/scottconverse/textflowkit/issues
@@ -21,11 +21,11 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
22
22
  Classifier: Topic :: Text Processing
23
23
  Requires-Python: >=3.10
24
- Requires-Dist: openai-whisper>=20240930
25
24
  Requires-Dist: yt-dlp>=2025.1.1
26
25
  Provides-Extra: all
27
26
  Requires-Dist: fastapi>=0.115; extra == 'all'
28
27
  Requires-Dist: mcp>=2.0; extra == 'all'
28
+ Requires-Dist: openai-whisper>=20240930; extra == 'all'
29
29
  Requires-Dist: pyannote-audio>=4.0; extra == 'all'
30
30
  Requires-Dist: pydantic>=2.7; extra == 'all'
31
31
  Requires-Dist: python-docx>=1.1; extra == 'all'
@@ -53,6 +53,8 @@ Requires-Dist: pydantic>=2.7; extra == 'http'
53
53
  Requires-Dist: uvicorn>=0.30; extra == 'http'
54
54
  Provides-Extra: mcp
55
55
  Requires-Dist: mcp>=2.0; extra == 'mcp'
56
+ Provides-Extra: whisper
57
+ Requires-Dist: openai-whisper>=20240930; extra == 'whisper'
56
58
  Description-Content-Type: text/markdown
57
59
 
58
60
  # textflowkit
@@ -62,9 +64,10 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
62
64
  [Project landing page](https://www.textflowkit.org/) ·
63
65
  [PyPI package](https://pypi.org/project/textflowkit/) ·
64
66
  [GitHub releases](https://github.com/scottconverse/textflowkit/releases) ·
65
- [User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
67
+ [User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md) ·
68
+ [Developer and integration manual](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md)
66
69
 
67
- **Current release: [v0.1.8](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8).**
70
+ **Current release: [v0.1.10](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.10).**
68
71
 
69
72
  The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
70
73
  Pages. GitHub remains the source and CI host; the website does not run the
@@ -111,7 +114,7 @@ URL or file ─► detect platform ─► acquire media ─► ffmpeg
111
114
  │
112
115
  ┌───────────────────────────┘
113
116
  ▼
114
- speech-to-text (Whisper)
117
+ speech-to-text (Whistle default; Whisper on request)
115
118
  │
116
119
  ▼
117
120
  canonical transcript (JSON)
@@ -125,8 +128,11 @@ DOCX and PDF are available through the optional `export` extra.
125
128
 
126
129
  ## Architecture
127
130
 
128
- The design principle is **one engine, three doors**. Everything of substance lives in
129
- the core; the interfaces are thin.
131
+ ![TextFlowKit shared-core architecture: five thin entry points feed one core. The CLI, Python API, MCP, HTTP API, and the local browser UI (a fifth door that mounts the existing HTTP app under /api and shares the same job store, adding transport only, not a new pipeline) sit above one core that acquires and decodes media, transcribes with the default Whistle engine or an explicitly selected openai-whisper, keeps job state and resume checkpoints in an optional SQLite store, adds optional speaker or translation postprocessing, and publishes TXT, SRT, VTT, JSON, Markdown, and optional DOCX/PDF exports.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.10/docs/assets/architecture-overview.svg)
132
+
133
+ The design principle is **one shared core, thin doors**. Everything of substance
134
+ lives in the core; the interfaces are thin — the local browser UI is a fifth door
135
+ that adds only transport over the existing HTTP app and shared job store.
130
136
 
131
137
  | Layer | Path | Responsibility |
132
138
  |---|---|---|
@@ -136,6 +142,7 @@ the core; the interfaces are thin.
136
142
  | **CLI** | `src/textflowkit/cli.py` | Reference interface (subprocess-friendly) |
137
143
  | **MCP** | `src/textflowkit/adapters/mcp_server.py` | stdio + Streamable HTTP, for AI harnesses |
138
144
  | **HTTP** | `src/textflowkit/adapters/http_server.py` | JSON API, for software products and web frontends |
145
+ | **Local UI** | `src/textflowkit/ui` | Loopback-only browser workspace over the same HTTP app and job store |
139
146
 
140
147
  Because the core owns the pipeline, adding a door is cheap — and adding a platform
141
148
  means writing one source adapter, not another tool.
@@ -164,6 +171,22 @@ python -m pip install textflowkit
164
171
  textflowkit doctor
165
172
  ```
166
173
 
174
+ The default engine is **Whistle**: a native CPU-only transcription CLI that
175
+ needs no PyTorch. It downloads one pinned model (about 17 MB) and a small pinned
176
+ binary on first use; a fresh default install therefore stays small and pulls no
177
+ torch. To use the `openai-whisper` engine instead — the ROCm/CUDA/CPU torch
178
+ stack — install the `whisper` extra and name it explicitly:
179
+
180
+ ```bash
181
+ python -m pip install 'textflowkit[whisper]'
182
+ textflowkit transcribe meeting.mp4 --engine whisper --model small
183
+ ```
184
+
185
+ See [Whistle (default engine)](#whistle-default-engine) for platform and
186
+ language coverage, and the [install guide](https://github.com/scottconverse/textflowkit/blob/main/docs/install.md)
187
+ for the AMD ROCm path, which applies to the explicit `whisper` engine and
188
+ diarization.
189
+
167
190
  For MCP or the JSON HTTP adapter, install the matching extra:
168
191
 
169
192
  ```bash
@@ -187,7 +210,6 @@ from textflowkit import transcribe
187
210
 
188
211
  result = transcribe(
189
212
  "meeting.mp4", # also accepts supported URLs
190
- model="small",
191
213
  formats=["json", "srt", "txt"],
192
214
  output_dir="transcripts", # omit to return the transcript without writing files
193
215
  )
@@ -200,6 +222,10 @@ for segment in result.transcript.segments:
200
222
  print(" ", word.start, word.end, word.text)
201
223
  ```
202
224
 
225
+ `engine` defaults to the product default (Whistle) and `model=None` resolves to
226
+ that engine's own model. To use the Whisper-family engines, name one and its
227
+ model explicitly: `transcribe("meeting.mp4", engine="whisper", model="small")`.
228
+
203
229
  `transcribe()` returns `TranscribeResult` with a canonical `Transcript` and
204
230
  written output paths. `Transcript.to_dict()` / `.to_json()` preserve segment and
205
231
  word timing; older transcript JSON without `words` remains readable. Pass
@@ -264,6 +290,60 @@ does **not** assert that the remote bytes are still identical.
264
290
  textflowkit export ./transcript.json --format vtt
265
291
  ```
266
292
 
293
+ ## Whistle (default engine)
294
+
295
+ **Whistle is the default engine** as of v0.1.9. It is a native CPU-only CLI that
296
+ needs no PyTorch, so a fresh `pip install textflowkit` stays small and pulls no
297
+ torch; `openai-whisper` is an explicitly selectable opt-in engine.
298
+
299
+ ![Whistle bounded block run and durable resume: a decoded PCM WAV is split into 26-second cores with up to 2 seconds of context (each clip ≤ 30 s), each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.10/docs/assets/whistle-resume.svg)
300
+
301
+ - **CPU only, no torch.** It runs a pinned native binary and downloads one pinned
302
+ model on first use. Naming a GPU device is refused rather than silently
303
+ ignored: a GPU request names the explicit `whisper` engine as the alternative,
304
+ and the engine is never switched for you.
305
+ - **Seven advertised languages:** `en`, `de`, `fr`, `es`, `it`, `nl`, `pl`. A
306
+ request for another language is refused at request construction, before any
307
+ media is fetched.
308
+ - **Native platforms:** Windows x86-64 and arm64, Linux x86-64 and arm64, and
309
+ Apple Silicon. **Intel Macs are not supported**; a request there is refused
310
+ with a message that names the explicit `whisper` engine instead. No WSL layer
311
+ is involved.
312
+ - **How it works:** long audio is split into 26-second cores with up to 2 seconds
313
+ of context on each side, so every standalone clip is ≤ 30 seconds (the native
314
+ CLI's limit). Overlap words are selected by core midpoint rather than text
315
+ deduplication, so repeated spoken phrases are preserved.
316
+ - **Streaming is not used.** The native `--audio-stream` mode failed its
317
+ coverage and is never passed; only the committed standalone output is parsed.
318
+ - **Durable block resume.** With `TEXTFLOWKIT_DB` set, a long run writes partial
319
+ per-block checkpoints; a resume checks the source and decoded-WAV hashes and the
320
+ config, then re-runs only the blocks that were not finished. The durable store
321
+ still assumes one owning process.
322
+ - **Cancellation.** A running Whistle run terminates its exact owned child
323
+ process bounded by a per-clip timeout. A single long `openai-whisper` model call
324
+ is still only cancellable at its next stage boundary, as documented for the
325
+ Whisper engine.
326
+ - **Telemetry is off, unconditionally.** Every Whistle child process forces
327
+ `NEEDLE_TELEMETRY=0`, `DO_NOT_TRACK=1`, and `CI=1`, overriding a parent that
328
+ opted in. The product ships no analytics, usage SDK, anonymous ids, or events,
329
+ and there is no opt-in setting. This applies the upstream documented gate; it
330
+ is **not** a claim that the upstream binary's tracking code is physically
331
+ removed.
332
+ - **Offline and downloads.** Set `TEXTFLOWKIT_OFFLINE` to refuse any download, and
333
+ `TEXTFLOWKIT_MODELS_DIR` to choose the asset directory. The Whistle asset helper
334
+ downloads only pinned runtime/model assets; media acquisition and optional
335
+ translation may independently use the network. These downloads are not
336
+ telemetry.
337
+
338
+ **Verification boundary.** A local run transcribed a 4-hour (14,407 s)
339
+ recording on CPU into 555 clips / 34,596 words / 2,875 segments in 731 s, with
340
+ telemetry forced off and offline mode on; a separate run proved durable block
341
+ resume across two processes. These are **first local test numbers**, not a
342
+ promise of universal performance or of accuracy equal to Whisper — no
343
+ human-scored word-error rate is claimed. See the
344
+ [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
345
+ and [install guide](https://github.com/scottconverse/textflowkit/blob/main/docs/install.md).
346
+
267
347
  ## Use as an MCP server
268
348
 
269
349
  ```bash
@@ -319,11 +399,71 @@ contract checks cover it, and running an actual build is deliberately out of
319
399
  scope here, so no image is claimed to build or start. See
320
400
  [the container example](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#container-example-dockerfile-and-compose).
321
401
 
402
+ ## Local browser interface
403
+
404
+ For point-and-click use there is a local browser workspace that runs on **this
405
+ machine only**, reuses the same pipeline and job store, and adds no dependency of
406
+ its own beyond the `http` extra. It ships with the core package as of v0.1.10:
407
+ a regular install of the current release carries it.
408
+
409
+ ```bash
410
+ python -m pip install 'textflowkit[http,export]==0.1.10'
411
+ textflowkit-ui
412
+ ```
413
+
414
+ On Windows you can create a desktop shortcut when you want one:
415
+
416
+ ```powershell
417
+ textflowkit-ui --create-shortcut # Start Menu entry (pythonw, no console)
418
+ textflowkit-ui --create-shortcut --shortcut-dir 'C:\path\to\folder'
419
+ ```
420
+
421
+ The shortcut is created only on request and needs no `pywin32`. Under `pythonw`
422
+ there is no console, so a startup failure is written to a log file under the
423
+ per-user data directory and shown in a message box rather than failing silently.
424
+ The normal operator path is to launch from that shortcut; the console command
425
+ above is equivalent.
426
+
427
+ **This is a local app, not a public website.** The [landing page](https://www.textflowkit.org/)
428
+ is static documentation served by Cloudflare Pages; it does **not** run the
429
+ transcription engine and offers no upload form. The browser workspace exists only
430
+ on the machine that installed it.
431
+
432
+ - **Loopback only.** It binds `127.0.0.1` (never off the machine, whatever
433
+ `TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a browser once it is serving.
434
+ - **One owner per database.** A second launch against the same database opens the
435
+ UI already running rather than starting a competing server. **Stop server** in
436
+ the workspace shuts the local server down without a terminal, waiting for the
437
+ current jobs to finish rather than interrupting them. Shutdown **drains** — it
438
+ lets in-flight jobs complete; it does **not** auto-cancel them. Cancel a job
439
+ first to stop it early.
440
+ - **Durable jobs with cancel and resume.** Jobs and checkpoints are stored
441
+ durably under the per-user data directory unless you set `TEXTFLOWKIT_DB` /
442
+ `TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT`. A dropped file is capped
443
+ at **2 GiB** by default (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it).
444
+ In-workspace playback uses the browser's native player on the **currently
445
+ selected** media, so it depends on what that browser can decode; it is a
446
+ convenience preview, not a format guarantee.
447
+ - **No UI analytics.** The page loads no CDN asset and no analytics, and the
448
+ session token lives only in the page — never in a URL, cookie, or log line.
449
+ - **What uses the network.** The page itself loads nothing remote. A **URL
450
+ source** is downloaded from the internet; an engine's **model** may be
451
+ downloaded on first use; and **remote or cloud translation** (an advanced
452
+ option) sends transcript text to that backend. With local media and a local
453
+ translation model, no transcript leaves the machine.
454
+ - `--port`, `--no-browser`, and `--version` are available.
455
+
456
+ The other doors are unchanged: the CLI, Python API, MCP, and HTTP adapters keep
457
+ their existing behavior, and the optional engines, diarization, and translation
458
+ retain their documented dependency requirements. See the
459
+ [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md#7-local-browser-interface)
460
+ and [architecture guide](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md#one-owner-per-database).
461
+
322
462
  ## Durable, bounded, cancellable
323
463
 
324
464
  ```bash
325
465
  TEXTFLOWKIT_DB=./jobs.db # job state survives restart (SQLite)
326
- TEXTFLOWKIT_MAX_CONCURRENCY=1 # default; Whisper saturates a GPU alone
466
+ TEXTFLOWKIT_MAX_CONCURRENCY=1 # default; bounded job worker pool
327
467
  ```
328
468
 
329
469
  `cancel_job` stops a queued job immediately, or a running job at its next stage
@@ -333,19 +473,51 @@ boundary. See [docs/adapters.md](https://github.com/scottconverse/textflowkit/bl
333
473
 
334
474
  The MCP and HTTP adapters are **job-based**: submission returns a job id
335
475
  immediately and clients poll for completion. The CLI uses the same core but
336
- waits for the result. This lets AI harnesses, software products, and a future
337
- web frontend share the job contract without blocking a request.
476
+ waits for the result. This lets AI harnesses, software products, and the local
477
+ browser interface share the job contract without blocking a request.
338
478
 
339
479
  ## Status
340
480
 
341
- **v0.1.8 release.** Core, CLI, MCP, and HTTP have automated
342
- coverage. This release carries the four fixes for the 2026-10-01 post-release
343
- audit-lite (findings AL-001 – AL-004): CLI owning-process startup recovery so an
344
- interrupted run's saved work can be resumed instead of being refused as
345
- "already active", preservation of the completed transcript across resume setup
346
- failures, a correction to the MCP batch cookie capability, and corrected roadmap
347
- evidence receipts. It builds on the v0.1.7 audit repair set and the v0.1.6
348
- review repairs that precede it. The preceding v0.1.7 source candidate was verified with real CLI, HTTP and MCP speech, seven-format exports and completed resume. The v0.1.8 runtime fixes were independently checked through fresh-process CLI restart probes, real SQLite setup-failure tests, and the full Windows test suite; those checks are not new individual-harness receipts. Release publication uses the tag workflow, which requires successful exact-commit main CI before PyPI uploads and creates the public GitHub release only afterward. Check the linked release for artifacts and workflow status; local source verification is not a fresh PyPI-install or individual-harness receipt.
481
+ **v0.1.10 release.** Core, CLI, MCP, HTTP, and the local browser interface have
482
+ automated coverage. This release **ships the local browser interface**
483
+ (`textflowkit-ui`) in the core package: a loopback-only workspace that reuses the
484
+ same pipeline, submission contract, and job store, with a durable store, cancel
485
+ and resume, and a Windows desktop shortcut (`textflowkit-ui --create-shortcut`).
486
+ It adds **no new dependency** beyond the existing `http` extra, so a regular
487
+ `pip install 'textflowkit[http,export]==0.1.10'` carries it. The engine, CLI,
488
+ Python, MCP, and HTTP surfaces are unchanged and there is **no endpoint-breaking
489
+ change**. It builds on v0.1.9, which made **Whistle the default engine** — a
490
+ native CPU-only transcriber that needs no PyTorch — and moved `openai-whisper` to
491
+ the optional `whisper` extra, selected explicitly with `--engine whisper` or
492
+ `engine="whisper"`. Legacy engine aliases are preserved, so older saved jobs and
493
+ `transcribe()` calls still decode. Whistle refuses an unsupported platform,
494
+ language, or GPU request instead of silently falling back; its engine's
495
+ verification boundary is in [Whistle (default engine)](#whistle-default-engine)
496
+ above.
497
+
498
+ **Verification limit.** The local browser interface is covered by an automated
499
+ suite, and the UI's behavior was proved on a **native Windows** machine in a real
500
+ browser. No live optional-backend (translation/diarization) run through the UI is
501
+ claimed, and **no live Linux or macOS UI run was performed here** — those
502
+ platforms are covered by automated tests only, and CI across Windows, Linux, and
503
+ macOS is the coordinator's separate step. Release publication uses the tag
504
+ workflow, which requires successful exact-commit main CI before PyPI uploads and
505
+ creates the public GitHub release only afterward. Check the linked release for
506
+ artifacts and workflow status; local source verification is not a fresh
507
+ PyPI-install or individual-harness receipt.
508
+
509
+ The v0.1.8 release carried four fixes for the 2026-10-01 post-release audit-lite
510
+ (findings AL-001 – AL-004): CLI owning-process startup recovery so an interrupted
511
+ run's saved work can be resumed instead of being refused as "already active",
512
+ preservation of the completed transcript across resume setup failures, a
513
+ correction to the MCP batch cookie capability, and corrected roadmap evidence
514
+ receipts. It built on the v0.1.7 audit repair set and the v0.1.6 review repairs
515
+ that precede it. The v0.1.7 source candidate was verified with real CLI, HTTP and
516
+ MCP speech, seven-format exports and completed resume; the v0.1.8 runtime fixes
517
+ were independently checked through fresh-process CLI restart probes, real SQLite
518
+ setup-failure tests, and the full Windows test suite. Those checks are not new
519
+ individual-harness receipts. That is the previous releases' record, not evidence
520
+ for v0.1.10.
349
521
 
350
522
  The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
351
523
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
@@ -362,9 +534,9 @@ The v0.1.6 release is public: the
362
534
  [v0.1.6 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6)
363
535
  and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
364
536
  [fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
365
- live — that is the previous release's evidence, not a receipt for v0.1.8.
537
+ live — that is the v0.1.6 release's evidence, not a receipt for a later release.
366
538
  Everything from here to the end of this section is the v0.1.6 release's
367
- historical record as published: it is not re-verified for v0.1.8, whose separate release evidence is not supplied by these historical paragraphs.
539
+ historical record as published: it is not re-verified for v0.1.10, whose separate release evidence is not supplied by these historical paragraphs.
368
540
  Merged-main CI passed 16/16 on the tagged commit; the published wheel and
369
541
  sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
370
542
  Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI