textflowkit 0.1.7__tar.gz → 0.1.8__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 (168) hide show
  1. {textflowkit-0.1.7 → textflowkit-0.1.8}/.github/workflows/publish-pypi.yml +17 -5
  2. {textflowkit-0.1.7 → textflowkit-0.1.8}/CHANGELOG.md +53 -4
  3. {textflowkit-0.1.7 → textflowkit-0.1.8}/PKG-INFO +12 -12
  4. {textflowkit-0.1.7 → textflowkit-0.1.8}/README.md +11 -11
  5. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/adapters.md +10 -9
  6. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/index.html +3 -3
  7. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/install.md +1 -1
  8. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/release-checklist.md +10 -4
  9. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/roadmap.md +39 -20
  10. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/user-manual.md +21 -5
  11. {textflowkit-0.1.7 → textflowkit-0.1.8}/pyproject.toml +1 -1
  12. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/__init__.py +1 -1
  13. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/cli.py +44 -0
  14. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/pipeline.py +44 -27
  15. textflowkit-0.1.8/tests/test_cli_owning_recovery.py +300 -0
  16. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_fetch_published_fonts.py +41 -2
  17. textflowkit-0.1.8/tests/test_mcp_batch_cookie_docs.py +98 -0
  18. textflowkit-0.1.8/tests/test_pipeline_resume_setup.py +266 -0
  19. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_publish_fonts_wiring.py +54 -4
  20. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_publish_manifest_wiring.py +95 -0
  21. {textflowkit-0.1.7 → textflowkit-0.1.8}/.dockerignore +0 -0
  22. {textflowkit-0.1.7 → textflowkit-0.1.8}/.env.example +0 -0
  23. {textflowkit-0.1.7 → textflowkit-0.1.8}/.gitattributes +0 -0
  24. {textflowkit-0.1.7 → textflowkit-0.1.8}/.github/dependabot.yml +0 -0
  25. {textflowkit-0.1.7 → textflowkit-0.1.8}/.github/workflows/ci.yml +0 -0
  26. {textflowkit-0.1.7 → textflowkit-0.1.8}/.gitignore +0 -0
  27. {textflowkit-0.1.7 → textflowkit-0.1.8}/.pre-commit-config.yaml +0 -0
  28. {textflowkit-0.1.7 → textflowkit-0.1.8}/CONTRIBUTING.md +0 -0
  29. {textflowkit-0.1.7 → textflowkit-0.1.8}/Dockerfile +0 -0
  30. {textflowkit-0.1.7 → textflowkit-0.1.8}/LEGAL.md +0 -0
  31. {textflowkit-0.1.7 → textflowkit-0.1.8}/LICENSE +0 -0
  32. {textflowkit-0.1.7 → textflowkit-0.1.8}/SECURITY.md +0 -0
  33. {textflowkit-0.1.7 → textflowkit-0.1.8}/compose.yaml +0 -0
  34. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/.nojekyll +0 -0
  35. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/404.html +0 -0
  36. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/architecture.md +0 -0
  37. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/site-deployment.md +0 -0
  38. {textflowkit-0.1.7 → textflowkit-0.1.8}/docs/sources.md +0 -0
  39. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/fetch_published_fonts.py +0 -0
  40. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/smoke_installed_wheel.py +0 -0
  41. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/smoke_live_youtube.py +0 -0
  42. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/verify_readme_release.py +0 -0
  43. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/verify_release_ci.py +0 -0
  44. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/verify_release_versions.py +0 -0
  45. {textflowkit-0.1.7 → textflowkit-0.1.8}/scripts/write_release_manifest.py +0 -0
  46. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/adapters/__init__.py +0 -0
  47. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/adapters/http_server.py +0 -0
  48. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/adapters/mcp_server.py +0 -0
  49. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/assets/README.md +0 -0
  50. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/assets/selftest-speech.wav +0 -0
  51. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/__init__.py +0 -0
  52. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/batch.py +0 -0
  53. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/bind.py +0 -0
  54. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/cancel.py +0 -0
  55. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/checkpoint.py +0 -0
  56. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/diarize.py +0 -0
  57. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/engine.py +0 -0
  58. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/executor.py +0 -0
  59. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/jobs.py +0 -0
  60. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/model.py +0 -0
  61. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/paths.py +0 -0
  62. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/retrieval.py +0 -0
  63. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/runner.py +0 -0
  64. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/service.py +0 -0
  65. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/sqlite_store.py +0 -0
  66. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/startup.py +0 -0
  67. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/submission.py +0 -0
  68. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/timeutil.py +0 -0
  69. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/core/translate.py +0 -0
  70. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/__init__.py +0 -0
  71. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/_cue_layout.py +0 -0
  72. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/_cue_text.py +0 -0
  73. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/_rename_excl.py +0 -0
  74. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/_rename_noreplace.py +0 -0
  75. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/docx.py +0 -0
  76. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/markdown.py +0 -0
  77. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/pdf.py +0 -0
  78. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/srt.py +0 -0
  79. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/txt.py +0 -0
  80. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/render/vtt.py +0 -0
  81. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/sources/__init__.py +0 -0
  82. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/sources/acquire.py +0 -0
  83. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/sources/detect.py +0 -0
  84. {textflowkit-0.1.7 → textflowkit-0.1.8}/src/textflowkit/sources/scratch.py +0 -0
  85. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/__init__.py +0 -0
  86. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/ollama_stub.py +0 -0
  87. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_acquire.py +0 -0
  88. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_adapters.py +0 -0
  89. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_admission_ownership.py +0 -0
  90. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_atomic_resume.py +0 -0
  91. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_atomic_resume_aba.py +0 -0
  92. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_batch.py +0 -0
  93. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_batch_item_boundary.py +0 -0
  94. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_batch_resume_shared.py +0 -0
  95. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_bind.py +0 -0
  96. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cancellation_finalization.py +0 -0
  97. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_checkpoint.py +0 -0
  98. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_checkpoint_single_copy.py +0 -0
  99. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cli.py +0 -0
  100. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cli_batch_feedback.py +0 -0
  101. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cli_resume_batch.py +0 -0
  102. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cli_stage_feedback.py +0 -0
  103. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cli_stdout_format.py +0 -0
  104. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_confined_decoder_boundary.py +0 -0
  105. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_confined_indirect_media.py +0 -0
  106. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_confined_resume_boundary.py +0 -0
  107. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_container_example.py +0 -0
  108. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_cue_layout.py +0 -0
  109. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_diarize.py +0 -0
  110. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_diarize_example_flags.py +0 -0
  111. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_diarize_token_docs.py +0 -0
  112. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_docx_publication_identity.py +0 -0
  113. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_done_output_integrity.py +0 -0
  114. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_empty_formats_resume.py +0 -0
  115. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_engine_adapters.py +0 -0
  116. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_engine_cache.py +0 -0
  117. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_engine_faster.py +0 -0
  118. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_engine_quiet.py +0 -0
  119. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_executor.py +0 -0
  120. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_executor_store_failure.py +0 -0
  121. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_export.py +0 -0
  122. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_export_file_mode.py +0 -0
  123. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_export_preflight.py +0 -0
  124. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_faster_whisper_docs.py +0 -0
  125. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_font_notices.py +0 -0
  126. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_http_host_boundary.py +0 -0
  127. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_http_trusted_proxy.py +0 -0
  128. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_job_progress.py +0 -0
  129. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_jobs.py +0 -0
  130. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_landing_commands.py +0 -0
  131. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_landing_page.py +0 -0
  132. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_legal_summary.py +0 -0
  133. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_linux_no_hardlink.py +0 -0
  134. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_live_smoke_script.py +0 -0
  135. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_macos_no_hardlink.py +0 -0
  136. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_mcp_http_boundary.py +0 -0
  137. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_mcp_protocol_cleanliness.py +0 -0
  138. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_mcp_terminal_guidance.py +0 -0
  139. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_mcp_tool_descriptions.py +0 -0
  140. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_mcp_work_root.py +0 -0
  141. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_model.py +0 -0
  142. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_partial_output_resume.py +0 -0
  143. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_partial_pdf_resume.py +0 -0
  144. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_paths.py +0 -0
  145. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_pipeline_integrity.py +0 -0
  146. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_production_browser_cookies.py +0 -0
  147. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_publish_ci_gate.py +0 -0
  148. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_publish_workflow_gate.py +0 -0
  149. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_readme_layout.py +0 -0
  150. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_readme_release_guard.py +0 -0
  151. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_release_boundaries.py +0 -0
  152. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_release_manifest.py +0 -0
  153. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_release_surfaces.py +0 -0
  154. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_render.py +0 -0
  155. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_retrieval.py +0 -0
  156. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_sdist_allowlist.py +0 -0
  157. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_service_profile.py +0 -0
  158. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_site_not_found.py +0 -0
  159. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_sources.py +0 -0
  160. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_stage_reuse_resume.py +0 -0
  161. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_startup_recovery.py +0 -0
  162. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_stdio_protocol.py +0 -0
  163. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_submission.py +0 -0
  164. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_submission_validation.py +0 -0
  165. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_translate.py +0 -0
  166. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_translate_completeness.py +0 -0
  167. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_verify_release_versions.py +0 -0
  168. {textflowkit-0.1.7 → textflowkit-0.1.8}/tests/test_windows_no_hardlink.py +0 -0
@@ -149,12 +149,24 @@ jobs:
149
149
  packages-dir: dist/main/
150
150
 
151
151
  publish-github-release:
152
- # Deliberately no `if:` here. The implicit `success()` plus the default skip
153
- # propagation is the gate that matters: a core upload that failed or was
154
- # skipped for any reason skips this job too, so a failed release cannot end
155
- # in a public GitHub release. Adding a status check function here would
156
- # weaken that, not strengthen it.
152
+ # The release exists only to advertise a core version that is on PyPI, so it
153
+ # runs exactly on a successful core upload and nothing else. The condition is
154
+ # written out rather than left implicit for the same reason as the core
155
+ # upload above: a status check function must defeat skip propagation. An
156
+ # implicit `success()` here is still subject to it, and on a reused fonts
157
+ # release the workflow-wide propagation from the *skipped* fonts job reaches
158
+ # this job through `publish-main` and its skipped fonts dependency - v0.1.7
159
+ # initially skipped the release even though core reached PyPI, and the
160
+ # recovery published it.
161
+ #
162
+ # `always()` would also defeat the propagation, but it runs the job in a
163
+ # cancelled run too, so it is not used. Only a `success` core upload reaches
164
+ # the release: a failed, skipped or cancelled core upload, a cancelled run,
165
+ # and a build that did not succeed all leave no public release, so the
166
+ # release can never advertise a core version that is not on PyPI.
157
167
  needs: publish-main
168
+ if: >-
169
+ ${{ !cancelled() && needs.publish-main.result == 'success' }}
158
170
  runs-on: ubuntu-latest
159
171
  permissions:
160
172
  contents: write
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.1.8 — 2026-10-02
4
+
5
+ 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.
6
+
7
+ ### Engineering fixes
8
+
9
+ - **AL-001 — CLI owning-process startup recovery.** A CLI invocation that was
10
+ interrupted left its PENDING/RUNNING row active; because the CLI submits with
11
+ `background=False` it never started the recovering executor, so a later
12
+ `--resume` read the orphan as "already active" and refused a job whose expensive
13
+ work was saved. Recovery now runs once per owning process at the CLI's
14
+ process/store boundary (`cli.main`), before single or batch resume selection,
15
+ through the shared per-store owner. It is deliberately **not** an unconditional
16
+ reap in every `submit_request(background=False)`: an embedding process can hold
17
+ genuinely live work. A recovery write failure is reported as a clear error with
18
+ a nonzero exit, not a false success or a raw traceback. The documented
19
+ one-owning-process-per-database model is unchanged.
20
+ - **AL-002 — preserve the completed transcript across resume setup failures.** The
21
+ source checkpoint serialized the local transcript (still `None`) before
22
+ hydrating the previous transcript, so a directory/`mkdtemp` failure replaced a
23
+ durable completed transcript with `null` while still listing `transcribe` as
24
+ finished. Reuse is now hydrated and validated **before** any directory setup,
25
+ and a replacement snapshot can no longer drop a still-finished transcript. A
26
+ changed local source is still refused, stages are never falsely marked complete,
27
+ and a healthy retry reuses the recognized work.
28
+
29
+ ### Documentation corrections
30
+
31
+ - **AL-003 — MCP batch cookie capability.** The adapter guide said
32
+ `submit_batch_media` has no `cookies_from_browser` parameter. It does: the tool
33
+ accepts one shared value and forwards it to every request, and the production
34
+ refusal is enforced by the same shared guard as the other surfaces. The matrix
35
+ and paragraph (and the historical DOC-003 changelog text) were corrected, with a
36
+ schema-consistency regression test that reads the real SDK-generated tool schema.
37
+ - **AL-004 — roadmap evidence.** The POSIX file-mode and fonts-reuse follow-ups
38
+ are marked done against their exact receipts: current-main CI run 36961982098
39
+ (12 OS/Python test jobs + three installed-wheel jobs + Ruff) verifies POSIX modes
40
+ on Linux/macOS, and release-workflow run 36961162844 built the reused 0.1.6
41
+ fonts, skipped the fonts upload, and published core to PyPI. The GitHub-release
42
+ job skip, its manual recovery, and the PR #23 condition repair are recorded
43
+ without claiming the original workflow was all green or the repaired automatic
44
+ path has been live-tested. The v0.1.7 history and version are preserved.
45
+
3
46
  ## v0.1.7 — 2026-10-01
4
47
 
5
48
  The 2026-10-01 audit repair release. Core version: 0.1.7; unchanged optional fonts: 0.1.6. Release artifacts and publication status are recorded on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7). Local source-candidate verification does not establish installed-package or individual harness compatibility.
@@ -63,12 +106,17 @@ The 2026-10-01 audit repair release. Core version: 0.1.7; unchanged optional fon
63
106
  overlapping rather than clipped.
64
107
  - **DOC-003** — "same options" is replaced with a per-surface parameter matrix
65
108
  (HTTP `formats` array vs MCP comma-separated string; HTTP batch options are
66
- **per item**, MCP batch options are shared and expose **no**
67
- `cookies_from_browser` parameter), and the batch contract now states that 202
68
- means admission, not completion, with per-item indexed errors. MCP
109
+ **per item**, MCP batch options are shared), and the batch contract now states
110
+ that 202 means admission, not completion, with per-item indexed errors. MCP
69
111
  `sources` is documented as a list of **strings**: a non-string entry (including
70
112
  a nested object or a number) is that entry's own error, not a whole-call
71
113
  refusal, while a string remains valid.
114
+ *Correction (2026-10-01, audit-lite AL-003):* the v0.1.7 text of this entry
115
+ claimed the MCP batch tool exposes **no** `cookies_from_browser` parameter.
116
+ That was wrong. `submit_batch_media` accepts `cookies_from_browser` and
117
+ forwards the one shared value to every request it builds; the production
118
+ refusal is enforced by the same shared guard as the other surfaces. The
119
+ adapter guide was corrected to match the generated tool schema.
72
120
  - **DOC-004** — a full production settings reference (names, defaults, units,
73
121
  required fields) is added to the adapter guide, and the production refusal of
74
122
  browser-cookie submissions is stated across the adapter guide, the sources
@@ -96,5 +144,6 @@ for the complete list and completion criteria. The current repair goal still
96
144
  covers decoder confinement, job lifecycle, publication/resume, translation and
97
145
  batch correctness, major terminal guidance/documentation, and its final gate.
98
146
 
99
- This changelog starts with the v0.1.7 release notes; published historical
147
+ This changelog starts with the v0.1.8 release notes, followed by the v0.1.7
148
+ release notes; published historical
100
149
  release notes remain on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: textflowkit
3
- Version: 0.1.7
3
+ Version: 0.1.8
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
@@ -64,7 +64,7 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
64
64
  [GitHub releases](https://github.com/scottconverse/textflowkit/releases) ·
65
65
  [User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
66
66
 
67
- **Current release: [v0.1.7](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7).**
67
+ **Current release: [v0.1.8](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8).**
68
68
 
69
69
  The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
70
70
  Pages. GitHub remains the source and CI host; the website does not run the
@@ -338,14 +338,14 @@ web frontend share the job contract without blocking a request.
338
338
 
339
339
  ## Status
340
340
 
341
- **v0.1.7 release.** Core, CLI, MCP, and HTTP have automated
342
- coverage. This release is the 2026-10-01 audit repair set — the post-v0.1.6
343
- review follow-ups the owner accepted on 2026-10-01: a confined decoder boundary
344
- for rooted local inputs, atomic job-attempt ownership with atomic worker startup
345
- and cancellation finalization, DOCX completed-output identity, per-request
346
- completed-stage reuse, an aggregate export preflight, translation completeness
347
- and per-item batch validation, live terminal feedback, and major documentation
348
- corrections. Implementation was verified on a native-Windows source candidate with real CLI, HTTP and MCP speech, seven-format exports and completed resume. 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.
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.
349
349
 
350
350
  The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
351
351
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
@@ -362,9 +362,9 @@ The v0.1.6 release is public: the
362
362
  [v0.1.6 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6)
363
363
  and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
364
364
  [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.7.
365
+ live — that is the previous release's evidence, not a receipt for v0.1.8.
366
366
  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.7, whose separate release evidence is not supplied by these historical paragraphs.
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.
368
368
  Merged-main CI passed 16/16 on the tagged commit; the published wheel and
369
369
  sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
370
370
  Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
@@ -7,7 +7,7 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
7
7
  [GitHub releases](https://github.com/scottconverse/textflowkit/releases) ·
8
8
  [User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
9
9
 
10
- **Current release: [v0.1.7](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7).**
10
+ **Current release: [v0.1.8](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8).**
11
11
 
12
12
  The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
13
13
  Pages. GitHub remains the source and CI host; the website does not run the
@@ -281,14 +281,14 @@ web frontend share the job contract without blocking a request.
281
281
 
282
282
  ## Status
283
283
 
284
- **v0.1.7 release.** Core, CLI, MCP, and HTTP have automated
285
- coverage. This release is the 2026-10-01 audit repair set — the post-v0.1.6
286
- review follow-ups the owner accepted on 2026-10-01: a confined decoder boundary
287
- for rooted local inputs, atomic job-attempt ownership with atomic worker startup
288
- and cancellation finalization, DOCX completed-output identity, per-request
289
- completed-stage reuse, an aggregate export preflight, translation completeness
290
- and per-item batch validation, live terminal feedback, and major documentation
291
- corrections. Implementation was verified on a native-Windows source candidate with real CLI, HTTP and MCP speech, seven-format exports and completed resume. 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.
284
+ **v0.1.8 release.** Core, CLI, MCP, and HTTP have automated
285
+ coverage. This release carries the four fixes for the 2026-10-01 post-release
286
+ audit-lite (findings AL-001 – AL-004): CLI owning-process startup recovery so an
287
+ interrupted run's saved work can be resumed instead of being refused as
288
+ "already active", preservation of the completed transcript across resume setup
289
+ failures, a correction to the MCP batch cookie capability, and corrected roadmap
290
+ evidence receipts. It builds on the v0.1.7 audit repair set and the v0.1.6
291
+ 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.
292
292
 
293
293
  The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
294
294
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
@@ -305,9 +305,9 @@ The v0.1.6 release is public: the
305
305
  [v0.1.6 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6)
306
306
  and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
307
307
  [fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
308
- live — that is the previous release's evidence, not a receipt for v0.1.7.
308
+ live — that is the previous release's evidence, not a receipt for v0.1.8.
309
309
  Everything from here to the end of this section is the v0.1.6 release's
310
- historical record as published: it is not re-verified for v0.1.7, whose separate release evidence is not supplied by these historical paragraphs.
310
+ historical record as published: it is not re-verified for v0.1.8, whose separate release evidence is not supplied by these historical paragraphs.
311
311
  Merged-main CI passed 16/16 on the tagged commit; the published wheel and
312
312
  sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
313
313
  Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
@@ -1,6 +1,6 @@
1
1
  # Adapters and integration
2
2
 
3
- Current published release: [v0.1.7](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7).
3
+ Current published release: [v0.1.8](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8).
4
4
  Start with the [user manual](user-manual.md) for everyday use. Harness versions
5
5
  in the compatibility table below identify **historical test sessions**, not
6
6
  the version of TextFlowKit currently published.
@@ -105,16 +105,17 @@ HTTP batch item by hand.
105
105
  | Input | `source: string`, required | `source: string`, required | `sources: list[string]`, required |
106
106
  | `formats` | **list** of strings (e.g. `["json","srt"]`); default `json`,`srt`,`txt` | **comma-separated string** `"json,srt,txt"` | **comma-separated string** |
107
107
  | `language`, `output_dir`, `model`, `device`, `engine`, `diarize`, `translate_to` | same fields on the single item; on HTTP batch each item carries its **own** copy, applied only to that item | same | shared across all sources |
108
- | `cookies_from_browser` | accepted on the HTTP single `POST /jobs` and on each HTTP batch item | accepted | **not a supported parameter** - callers must not supply it |
108
+ | `cookies_from_browser` | accepted on the HTTP single `POST /jobs` and on each HTTP batch item | accepted | accepted (shared across all sources) |
109
109
  | Resume | batch wrapper `resume: bool`; single `POST /jobs/{id}/resume` | `resume_job` tool | `resume: bool` |
110
110
 
111
- The MCP batch tool's `cookies_from_browser` row is the one place the surfaces
112
- genuinely differ: `submit_batch_media`'s signature has no such parameter, so a
113
- caller cannot pass browser cookies through it — only through `transcribe_media`
114
- or an HTTP submission. On HTTP, batch options are **per item**: each entry of
115
- `jobs` is a full `TranscribeRequest`, so two items can name different models or
116
- languages. The MCP batch tool instead takes its options **once** and applies them
117
- to every source.
111
+ The `cookies_from_browser` value is accepted on every surface. On `transcribe_media`
112
+ and `submit_batch_media` it is one shared value applied to each request the call
113
+ builds; on HTTP it is **per item**: each entry of `jobs` is a full
114
+ `TranscribeRequest`, so two items can name different models or languages. The MCP
115
+ batch tool instead takes its options **once** and applies them to every source.
116
+ Whichever surface supplies it, a cookie request is refused by the shared
117
+ production guard when `TEXTFLOWKIT_PROFILE=production` (see the production
118
+ settings reference below); it is honoured only in the developer/owner profile.
118
119
 
119
120
  `output_dir` omitted writes no rendered files, but a durable transcript is still
120
121
  kept: with `TEXTFLOWKIT_DB` set the transcript survives the process, so "no
@@ -188,7 +188,7 @@
188
188
  <main id="main">
189
189
  <div class="wrap hero">
190
190
  <div>
191
- <span class="eyebrow">Open source · Apache 2.0 · v0.1.7</span>
191
+ <span class="eyebrow">Open source · Apache 2.0 · v0.1.8</span>
192
192
  <h1>Media in.<br><em>Useful text</em> out.</h1>
193
193
  <p class="lead">Transcribe video and audio into timestamped, searchable text. One shared core for your command line, AI harness, and application.</p>
194
194
  <div class="actions">
@@ -196,7 +196,7 @@
196
196
  <a class="button secondary" href="https://pypi.org/project/textflowkit/">Install from PyPI <span aria-hidden="true">&nbsp;↗</span></a>
197
197
  </div>
198
198
  <p class="micro">Self-hosted toolkit · Native Windows, macOS, Linux · No account required</p>
199
- <p class="micro">Latest verified release: <a href="https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7">v0.1.7</a> · <a href="https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md">User manual</a></p>
199
+ <p class="micro">Latest verified release: <a href="https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8">v0.1.8</a> · <a href="https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md">User manual</a></p>
200
200
  </div>
201
201
  <div class="terminal" aria-label="Example transcription command and output">
202
202
  <div class="terminal-top"><span class="dot"></span><span class="dot"></span><span class="dot"></span><span class="terminal-label">textflowkit / terminal</span></div>
@@ -276,7 +276,7 @@
276
276
  <li>Find the right setup for AMD ROCm in the install guide before installing dependencies.</li>
277
277
  </ul>
278
278
  <a class="button secondary" href="https://github.com/scottconverse/textflowkit/blob/main/docs/install.md">Read install guide ↗</a>
279
- <p class="note">New here? Follow the <a href="https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md">v0.1.7 user manual</a> for CLI, MCP, HTTP, export, and troubleshooting examples.</p>
279
+ <p class="note">New here? Follow the <a href="https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md">v0.1.8 user manual</a> for CLI, MCP, HTTP, export, and troubleshooting examples.</p>
280
280
  </div>
281
281
  <div>
282
282
  <pre class="codebox"><span class="prompt">$ </span>python -m pip install "textflowkit[mcp,http]"
@@ -1,6 +1,6 @@
1
1
  # Install notes
2
2
 
3
- Current release: [v0.1.7](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7).
3
+ Current release: [v0.1.8](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8).
4
4
  Use `textflowkit --version` to confirm the installed version. For everyday
5
5
  commands and outputs, start with the [user manual](user-manual.md).
6
6
 
@@ -75,7 +75,7 @@ receipt. It does not upload cookies, media, or transcripts to GitHub.
75
75
  ## PyPI publication
76
76
 
77
77
  After merged-main CI and the local release checks pass, push an annotated final
78
- version tag (for example `v0.1.7` for a future release) on that verified
78
+ version tag (for example `v0.1.8` for a future release) on that verified
79
79
  `main` commit. Do **not**
80
80
  create or publish a GitHub release manually. The tag push triggers
81
81
  [`publish-pypi.yml`](../.github/workflows/publish-pypi.yml). It verifies the
@@ -147,9 +147,15 @@ the version declared in `packages/textflowkit-fonts/pyproject.toml` and asks
147
147
  and the resolution reported a reuse, and it refuses to continue for a failed
148
148
  or cancelled fonts upload, a failed build, a cancelled run, or a skipped
149
149
  fonts job that was not a reuse. So a core release can never be published
150
- against a fonts version that is not on PyPI. The GitHub release job has no
151
- condition of its own, so a core upload that failed or was skipped leaves the
152
- release uncreated.
150
+ against a fonts version that is not on PyPI. The GitHub release job also
151
+ carries its own condition, for the same reason: the skip propagation from the
152
+ reused fonts job reaches it through `publish-main` and its skipped fonts
153
+ dependency, so an implicit `success()` would suppress the release on the reuse
154
+ path — v0.1.7 initially skipped the release even though core reached PyPI, and
155
+ the recovery published it. It runs only on a `success` core upload, using a
156
+ status check function so the reuse path is not suppressed, while a failed,
157
+ skipped or cancelled core upload or a cancelled run still leaves the release
158
+ uncreated.
153
159
  - **Anything else stops the release before it builds or uploads.** A transient
154
160
  or 5xx answer, a response that is not JSON or has no file list, a release whose
155
161
  files are not exactly one wheel and one sdist for that version (a partial
@@ -105,27 +105,46 @@ matched, and a fresh install passed self-test, transcription, and PDF export.
105
105
  identity, per-request completed-stage reuse, an aggregate export preflight,
106
106
  translation completeness and per-item batch validation, live terminal
107
107
  feedback, and the DOC-001 – DOC-006 documentation corrections. Implementation verification is complete; publication and its exact-commit CI are tracked by the [v0.1.7 release workflow](https://github.com/scottconverse/textflowkit/actions/workflows/publish-pypi.yml). Fonts remain 0.1.6 and are reused without republishing. Historical release evidence above remains scoped to its stated version.
108
- - [ ] Follow-up: normalize exported file permissions on POSIX to respect the
109
- process umask. Current atomic temporary files can leave outputs mode `0600`,
110
- preventing another account (such as a separate web server user) from reading
111
- exported subtitles or documents. Fix and regression-test separately.
112
- The mode is now measured and applied before publication, with regression
113
- tests for it, but the POSIX behaviour has not been verified on a live POSIX
114
- host yet, so this stays open until CI or a POSIX machine confirms it.
115
- - [ ] Follow-up: decouple the fonts package's version from core releases so
116
- unchanged font wheels are not rebuilt/uploaded every patch. The publish
117
- workflow now resolves the fonts version against PyPI before it builds: an
118
- unpublished version is built and uploaded, and a published one is fetched
119
- from PyPI with its recorded SHA-256 and size verified and is never rebuilt,
120
- re-uploaded, or hidden behind `skip-existing`. A changed fonts package must
121
- carry a new version, and the fonts upload and the core upload are conditional
122
- so the reuse path still publishes the core release. See the
108
+ - [x] Ship v0.1.8 review follow-ups: the 2026-10-01 post-release audit-lite
109
+ follow-ups (AL-001 – AL-004). AL-001 gives the CLI owning-process startup
110
+ recovery at its process/store boundary, so an interrupted job's saved work can
111
+ be resumed instead of being refused as "already active"; AL-002 validates and
112
+ rehydrates reusable work **before** publishing a checkpoint, so a resume setup
113
+ failure can no longer replace a durable completed transcript with an empty one;
114
+ AL-003 corrects the MCP batch cookie-capability documentation and its
115
+ schema-consistency test; AL-004 corrects roadmap evidence receipts. These are
116
+ the four fixes recorded for [v0.1.8](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8),
117
+ whose publication and exact-commit CI are tracked by the [release workflow](https://github.com/scottconverse/textflowkit/actions/workflows/publish-pypi.yml).
118
+ Fonts remain 0.1.6 and are reused without republishing. This entry does not
119
+ restate or rewrite the historical v0.1.7 achievements below, whose evidence
120
+ remains scoped to its stated version.
121
+ - [x] Normalize exported file permissions on POSIX to respect the process
122
+ umask. The mode is measured and applied before publication, with regression
123
+ tests for it. The POSIX behaviour is now verified on live POSIX hosts: the
124
+ Linux/macOS jobs of the current-main [CI run 36961982098](https://github.com/scottconverse/textflowkit/actions/runs/36961982098)
125
+ run the full suite (12 OS/Python test jobs plus three installed-wheel jobs
126
+ and Ruff) on the audited commit, and `tests/test_export_file_mode.py` carries
127
+ the six umask/publication cases (Windows-only excluded). This is no longer
128
+ unverified.
129
+ - [x] Decouple the fonts package's version from core releases so unchanged font
130
+ wheels are not rebuilt/uploaded every patch. The publish workflow resolves the
131
+ fonts version against PyPI before it builds: an unpublished version is built
132
+ and uploaded, and a published one is fetched from PyPI with its recorded
133
+ SHA-256 and size verified and is never rebuilt, re-uploaded, or hidden behind
134
+ `skip-existing`. A changed fonts package must carry a new version, and the
135
+ fonts upload and the core upload are conditional so the reuse path still
136
+ publishes the core release. See the
123
137
  [release checklist](release-checklist.md#the-fonts-version-contract-issue-15).
124
- This stays open until a release actually reuses a published fonts version on
125
- PyPI. v0.1.6 exercised only the new-fonts path, where 0.1.6 was not yet
126
- published and both packages were built and uploaded; the reuse path is still
127
- covered by deterministic tests over injected index responses, not by a live
128
- tag.
138
+ The reuse path is now exercised by a live release: the v0.1.7 build
139
+ ([release workflow run 36961162844](https://github.com/scottconverse/textflowkit/actions/runs/36961162844))
140
+ resolved the unchanged 0.1.6 fonts for reuse, **skipped** the fonts upload,
141
+ and published core to PyPI successfully. One nuance stays recorded rather than
142
+ papered over: that same run's GitHub-release job was skipped by a transitive
143
+ condition, and GitHub publication was recovered manually from the original
144
+ verified artifacts; PR #23 repaired the condition on main, so the repaired
145
+ future automatic GitHub-release path has deterministic tests, **not** a second
146
+ live tag run. This entry does not claim the original workflow was all green, nor
147
+ that the repaired publication path has been live-tested.
129
148
 
130
149
  The 13 listed media platforms are recognised through `yt-dlp`; **only YouTube**
131
150
  has an opt-in [live URL transcription release gate](release-checklist.md), not
@@ -1,4 +1,4 @@
1
- # TextFlowKit user manual — v0.1.7
1
+ # TextFlowKit user manual — v0.1.8
2
2
 
3
3
  TextFlowKit turns a local audio/video file or a supported media URL into a
4
4
  timestamped transcript. It is a **self-hosted developer tool**, not a hosted
@@ -12,7 +12,7 @@ Install Python 3.10 or later and `ffmpeg`/`ffprobe` on `PATH`. For a standard
12
12
  CPU setup, install the current release from PyPI:
13
13
 
14
14
  ```bash
15
- python -m pip install 'textflowkit[export,mcp,http]==0.1.7'
15
+ python -m pip install 'textflowkit[export,mcp,http]==0.1.8'
16
16
  textflowkit --version
17
17
  textflowkit doctor
18
18
  textflowkit selftest
@@ -105,6 +105,22 @@ match. Without `TEXTFLOWKIT_DB`, jobs are in memory and cannot survive a
105
105
  process restart. The job executor bounds concurrent work; consult
106
106
  [adapter and production settings](adapters.md) before running a service.
107
107
 
108
+ The durable store assumes **one owning process per database**. A CLI invocation
109
+ owns its store for the length of the command, so before it selects work to
110
+ resume it recovers rows left `pending` or `running` by a process that died:
111
+ those orphans have no worker, and without recovery a `--resume` reads them as
112
+ "already active" and refuses a job whose expensive work was saved. Run only one
113
+ process against a given `TEXTFLOWKIT_DB`; a second one would recover the first
114
+ one's live jobs. A recovery write that fails stops the command with an error
115
+ rather than continuing.
116
+
117
+ Resume preserves the completed transcript. Reusable work is validated and
118
+ rehydrated **before** any working-directory setup, so a setup failure (an
119
+ unusable work directory, a scratch directory that cannot be created) leaves the
120
+ previous durable checkpoint — including its finished transcript — intact for a
121
+ later retry instead of replacing it with an empty one. A changed local source is
122
+ still refused, and a stage is only ever listed as finished when it really is.
123
+
108
124
  ## 4. Export or inspect a saved transcript
109
125
 
110
126
  ```bash
@@ -244,11 +260,11 @@ See the [adapter guide](adapters.md#input-paths-unconfined-by-default) and
244
260
 
245
261
  ## 7. Release and help
246
262
 
247
- - [v0.1.7 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7)
248
- - [Core package 0.1.7 on PyPI](https://pypi.org/project/textflowkit/0.1.7/) and [unchanged optional font package 0.1.6](https://pypi.org/project/textflowkit-fonts/0.1.6/)
263
+ - [v0.1.8 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8)
264
+ - [Core package 0.1.8 on PyPI](https://pypi.org/project/textflowkit/0.1.8/) and [unchanged optional font package 0.1.6](https://pypi.org/project/textflowkit-fonts/0.1.6/)
249
265
  - [Release verification procedure](release-checklist.md), [security policy](../SECURITY.md), and [issues](https://github.com/scottconverse/textflowkit/issues)
250
266
 
251
- Local source-candidate verification covered real CLI/HTTP/MCP speech, exports and completed resume. See release artifacts and workflow results for publication evidence. The following paragraphs retain historical evidence for v0.1.6 and v0.1.5; they do not establish v0.1.7 installed-package or harness verification.
267
+ The preceding v0.1.7 source-candidate verification covered real CLI/HTTP/MCP speech, 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. See release artifacts and workflow results for publication evidence. The following paragraphs retain historical evidence for v0.1.6 and v0.1.5; they do not establish v0.1.8 installed-package or harness verification.
252
268
 
253
269
  The v0.1.6 release passed the Windows/Linux/macOS CI matrix on the tagged
254
270
  commit, and a fresh Windows Python 3.12 install of
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "textflowkit"
7
- version = "0.1.7"
7
+ version = "0.1.8"
8
8
  description = "Cross-platform media transcription toolkit: URL or file in, timestamped transcripts and subtitle files out."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -3,6 +3,6 @@
3
3
  from textflowkit.core.model import Segment, Transcript, WordTiming
4
4
  from textflowkit.core.pipeline import PipelineError, transcribe
5
5
 
6
- __version__ = "0.1.7"
6
+ __version__ = "0.1.8"
7
7
 
8
8
  __all__ = ["PipelineError", "Segment", "Transcript", "WordTiming", "__version__", "transcribe"]
@@ -18,6 +18,7 @@ from textflowkit.core.model import Transcript
18
18
  from textflowkit.core.paths import default_input_root, output_root
19
19
  from textflowkit.core.pipeline import TranscribeResult
20
20
  from textflowkit.core.runner import transcript_for
21
+ from textflowkit.core.startup import StartupRecoveryError, recover_startup
21
22
  from textflowkit.core.submission import SubmissionRequest, submit_request
22
23
  from textflowkit.render import (
23
24
  BINARY_FORMATS,
@@ -170,6 +171,38 @@ def _store_is_durable() -> bool:
170
171
  return bool(os.environ.get("TEXTFLOWKIT_DB"))
171
172
 
172
173
 
174
+ def _recover_owned_store() -> int:
175
+ """Recover orphaned rows once for the store this CLI process owns.
176
+
177
+ A CLI invocation owns its store for the duration of the command: the jobs
178
+ this process runs are created after this call, so any PENDING/RUNNING row
179
+ already in a durable store is an orphan from a process that died - it has no
180
+ worker, and without recovery a ``--resume`` reads it as "already active" and
181
+ refuses to reuse the saved work (audit finding AL-001).
182
+
183
+ This belongs at the CLI's process/store ownership boundary, *not* inside
184
+ ``submit_request(background=False)``: an embedding process can hold
185
+ genuinely live work, so an unconditional reap there would fail rows it does
186
+ not own. Recovery is delegated to the shared per-store owner, so a single
187
+ CLI process reaps exactly once even if both the transcribe and batch paths
188
+ were to ask. It runs only against a durable store; an in-memory store is
189
+ fresh per process and has no orphans to recover.
190
+
191
+ Returns 0 when recovery ran or was unnecessary, and 1 (with a clear message
192
+ on stderr, no traceback) when the store rejected the recovery write: the
193
+ command must fail rather than proceed over a store whose orphans still read
194
+ ``running``.
195
+ """
196
+ if not _store_is_durable():
197
+ return 0
198
+ try:
199
+ recover_startup(get_default_store())
200
+ except StartupRecoveryError as exc:
201
+ print(f"error: {exc}", file=sys.stderr)
202
+ return 1
203
+ return 0
204
+
205
+
173
206
  def _preflight_engine(name: str) -> str | None:
174
207
  """Reject an unusable ``--engine`` before any work is submitted.
175
208
 
@@ -569,8 +602,19 @@ def main(argv: list[str] | None = None) -> int:
569
602
  parser = _build_parser()
570
603
  args = parser.parse_args(argv)
571
604
  if args.command == "transcribe":
605
+ # Recover the durable store this process owns before resume selection,
606
+ # so an interrupted job from a dead process is reusable rather than
607
+ # refused as "already active". Only the commands that submit and resume
608
+ # work need this; `export`, `sources`, `doctor` and `selftest` neither
609
+ # own nor select jobs and are left with their existing side effects.
610
+ recovery = _recover_owned_store()
611
+ if recovery != 0:
612
+ return recovery
572
613
  return _cmd_transcribe(args)
573
614
  if args.command == "batch":
615
+ recovery = _recover_owned_store()
616
+ if recovery != 0:
617
+ return recovery
574
618
  return _cmd_batch(args)
575
619
  if args.command == "export":
576
620
  return _cmd_export(args)