textflowkit 0.1.6__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 (174) hide show
  1. {textflowkit-0.1.6 → textflowkit-0.1.8}/.github/workflows/publish-pypi.yml +17 -5
  2. textflowkit-0.1.8/CHANGELOG.md +149 -0
  3. {textflowkit-0.1.6 → textflowkit-0.1.8}/CONTRIBUTING.md +7 -0
  4. {textflowkit-0.1.6 → textflowkit-0.1.8}/PKG-INFO +37 -12
  5. {textflowkit-0.1.6 → textflowkit-0.1.8}/README.md +36 -11
  6. {textflowkit-0.1.6 → textflowkit-0.1.8}/SECURITY.md +57 -5
  7. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/adapters.md +179 -21
  8. textflowkit-0.1.8/docs/architecture.md +357 -0
  9. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/index.html +3 -3
  10. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/install.md +42 -22
  11. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/release-checklist.md +13 -7
  12. textflowkit-0.1.8/docs/roadmap.md +187 -0
  13. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/site-deployment.md +9 -9
  14. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/sources.md +8 -0
  15. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/user-manual.md +88 -16
  16. {textflowkit-0.1.6 → textflowkit-0.1.8}/pyproject.toml +4 -1
  17. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/__init__.py +1 -1
  18. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/adapters/http_server.py +107 -23
  19. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/adapters/mcp_server.py +154 -40
  20. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/cli.py +83 -6
  21. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/batch.py +22 -4
  22. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/checkpoint.py +40 -10
  23. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/engine.py +67 -0
  24. textflowkit-0.1.8/src/textflowkit/core/executor.py +791 -0
  25. textflowkit-0.1.8/src/textflowkit/core/jobs.py +675 -0
  26. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/paths.py +18 -2
  27. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/pipeline.py +179 -30
  28. textflowkit-0.1.8/src/textflowkit/core/runner.py +264 -0
  29. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/service.py +22 -9
  30. textflowkit-0.1.8/src/textflowkit/core/sqlite_store.py +585 -0
  31. textflowkit-0.1.8/src/textflowkit/core/startup.py +136 -0
  32. textflowkit-0.1.8/src/textflowkit/core/submission.py +540 -0
  33. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/translate.py +21 -0
  34. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/__init__.py +399 -29
  35. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/sources/acquire.py +100 -4
  36. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_adapters.py +22 -10
  37. textflowkit-0.1.8/tests/test_admission_ownership.py +375 -0
  38. textflowkit-0.1.8/tests/test_atomic_resume.py +615 -0
  39. textflowkit-0.1.8/tests/test_atomic_resume_aba.py +478 -0
  40. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_batch.py +30 -14
  41. textflowkit-0.1.8/tests/test_batch_item_boundary.py +645 -0
  42. textflowkit-0.1.8/tests/test_cancellation_finalization.py +1470 -0
  43. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_checkpoint.py +1 -1
  44. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_checkpoint_single_copy.py +1 -1
  45. textflowkit-0.1.8/tests/test_cli_batch_feedback.py +234 -0
  46. textflowkit-0.1.8/tests/test_cli_owning_recovery.py +300 -0
  47. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_cli_resume_batch.py +12 -2
  48. textflowkit-0.1.8/tests/test_cli_stage_feedback.py +373 -0
  49. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_cli_stdout_format.py +12 -0
  50. textflowkit-0.1.8/tests/test_confined_decoder_boundary.py +248 -0
  51. textflowkit-0.1.8/tests/test_confined_indirect_media.py +211 -0
  52. textflowkit-0.1.8/tests/test_confined_resume_boundary.py +153 -0
  53. textflowkit-0.1.8/tests/test_docx_publication_identity.py +994 -0
  54. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_done_output_integrity.py +1 -1
  55. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_empty_formats_resume.py +1 -1
  56. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_engine_adapters.py +59 -16
  57. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_engine_faster.py +12 -3
  58. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_engine_quiet.py +15 -2
  59. textflowkit-0.1.8/tests/test_executor_store_failure.py +1000 -0
  60. textflowkit-0.1.8/tests/test_export_preflight.py +683 -0
  61. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_fetch_published_fonts.py +41 -2
  62. textflowkit-0.1.8/tests/test_mcp_batch_cookie_docs.py +98 -0
  63. textflowkit-0.1.8/tests/test_mcp_protocol_cleanliness.py +215 -0
  64. textflowkit-0.1.8/tests/test_mcp_terminal_guidance.py +222 -0
  65. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_mcp_tool_descriptions.py +6 -2
  66. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_partial_output_resume.py +1 -1
  67. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_partial_pdf_resume.py +1 -1
  68. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_pipeline_integrity.py +1 -1
  69. textflowkit-0.1.8/tests/test_pipeline_resume_setup.py +266 -0
  70. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_production_browser_cookies.py +34 -5
  71. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_publish_fonts_wiring.py +54 -4
  72. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_publish_manifest_wiring.py +95 -0
  73. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_release_boundaries.py +13 -4
  74. textflowkit-0.1.8/tests/test_stage_reuse_resume.py +1315 -0
  75. textflowkit-0.1.8/tests/test_startup_recovery.py +622 -0
  76. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_submission.py +32 -14
  77. textflowkit-0.1.8/tests/test_submission_validation.py +311 -0
  78. textflowkit-0.1.8/tests/test_translate_completeness.py +351 -0
  79. textflowkit-0.1.6/docs/roadmap.md +0 -129
  80. textflowkit-0.1.6/src/textflowkit/core/executor.py +0 -297
  81. textflowkit-0.1.6/src/textflowkit/core/jobs.py +0 -241
  82. textflowkit-0.1.6/src/textflowkit/core/runner.py +0 -173
  83. textflowkit-0.1.6/src/textflowkit/core/sqlite_store.py +0 -233
  84. textflowkit-0.1.6/src/textflowkit/core/submission.py +0 -279
  85. {textflowkit-0.1.6 → textflowkit-0.1.8}/.dockerignore +0 -0
  86. {textflowkit-0.1.6 → textflowkit-0.1.8}/.env.example +0 -0
  87. {textflowkit-0.1.6 → textflowkit-0.1.8}/.gitattributes +0 -0
  88. {textflowkit-0.1.6 → textflowkit-0.1.8}/.github/dependabot.yml +0 -0
  89. {textflowkit-0.1.6 → textflowkit-0.1.8}/.github/workflows/ci.yml +0 -0
  90. {textflowkit-0.1.6 → textflowkit-0.1.8}/.gitignore +0 -0
  91. {textflowkit-0.1.6 → textflowkit-0.1.8}/.pre-commit-config.yaml +0 -0
  92. {textflowkit-0.1.6 → textflowkit-0.1.8}/Dockerfile +0 -0
  93. {textflowkit-0.1.6 → textflowkit-0.1.8}/LEGAL.md +0 -0
  94. {textflowkit-0.1.6 → textflowkit-0.1.8}/LICENSE +0 -0
  95. {textflowkit-0.1.6 → textflowkit-0.1.8}/compose.yaml +0 -0
  96. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/.nojekyll +0 -0
  97. {textflowkit-0.1.6 → textflowkit-0.1.8}/docs/404.html +0 -0
  98. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/fetch_published_fonts.py +0 -0
  99. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/smoke_installed_wheel.py +0 -0
  100. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/smoke_live_youtube.py +0 -0
  101. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/verify_readme_release.py +0 -0
  102. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/verify_release_ci.py +0 -0
  103. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/verify_release_versions.py +0 -0
  104. {textflowkit-0.1.6 → textflowkit-0.1.8}/scripts/write_release_manifest.py +0 -0
  105. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/adapters/__init__.py +0 -0
  106. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/assets/README.md +0 -0
  107. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/assets/selftest-speech.wav +0 -0
  108. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/__init__.py +0 -0
  109. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/bind.py +0 -0
  110. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/cancel.py +0 -0
  111. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/diarize.py +0 -0
  112. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/model.py +0 -0
  113. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/retrieval.py +0 -0
  114. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/core/timeutil.py +0 -0
  115. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/_cue_layout.py +0 -0
  116. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/_cue_text.py +0 -0
  117. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/_rename_excl.py +0 -0
  118. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/_rename_noreplace.py +0 -0
  119. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/docx.py +0 -0
  120. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/markdown.py +0 -0
  121. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/pdf.py +0 -0
  122. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/srt.py +0 -0
  123. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/txt.py +0 -0
  124. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/render/vtt.py +0 -0
  125. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/sources/__init__.py +0 -0
  126. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/sources/detect.py +0 -0
  127. {textflowkit-0.1.6 → textflowkit-0.1.8}/src/textflowkit/sources/scratch.py +0 -0
  128. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/__init__.py +0 -0
  129. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/ollama_stub.py +0 -0
  130. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_acquire.py +0 -0
  131. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_batch_resume_shared.py +0 -0
  132. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_bind.py +0 -0
  133. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_cli.py +0 -0
  134. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_container_example.py +0 -0
  135. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_cue_layout.py +0 -0
  136. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_diarize.py +0 -0
  137. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_diarize_example_flags.py +0 -0
  138. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_diarize_token_docs.py +0 -0
  139. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_engine_cache.py +0 -0
  140. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_executor.py +0 -0
  141. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_export.py +0 -0
  142. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_export_file_mode.py +0 -0
  143. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_faster_whisper_docs.py +0 -0
  144. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_font_notices.py +0 -0
  145. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_http_host_boundary.py +0 -0
  146. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_http_trusted_proxy.py +0 -0
  147. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_job_progress.py +0 -0
  148. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_jobs.py +0 -0
  149. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_landing_commands.py +0 -0
  150. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_landing_page.py +0 -0
  151. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_legal_summary.py +0 -0
  152. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_linux_no_hardlink.py +0 -0
  153. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_live_smoke_script.py +0 -0
  154. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_macos_no_hardlink.py +0 -0
  155. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_mcp_http_boundary.py +0 -0
  156. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_mcp_work_root.py +0 -0
  157. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_model.py +0 -0
  158. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_paths.py +0 -0
  159. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_publish_ci_gate.py +0 -0
  160. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_publish_workflow_gate.py +0 -0
  161. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_readme_layout.py +0 -0
  162. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_readme_release_guard.py +0 -0
  163. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_release_manifest.py +0 -0
  164. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_release_surfaces.py +0 -0
  165. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_render.py +0 -0
  166. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_retrieval.py +0 -0
  167. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_sdist_allowlist.py +0 -0
  168. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_service_profile.py +0 -0
  169. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_site_not_found.py +0 -0
  170. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_sources.py +0 -0
  171. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_stdio_protocol.py +0 -0
  172. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_translate.py +0 -0
  173. {textflowkit-0.1.6 → textflowkit-0.1.8}/tests/test_verify_release_versions.py +0 -0
  174. {textflowkit-0.1.6 → 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
@@ -0,0 +1,149 @@
1
+ # Changelog
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
+
46
+ ## v0.1.7 — 2026-10-01
47
+
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.
49
+
50
+ ### Engineering fixes
51
+
52
+ - **Confined decoder boundary.** With `TEXTFLOWKIT_INPUT_ROOT` set, a confined
53
+ local input is staged and its decode is restricted to FFmpeg's self-contained
54
+ demuxers (`-format_whitelist`), and the `ffprobe` duration probe carries the
55
+ same whitelist. A playlist/manifest that would make the decoder open referenced
56
+ files is refused. The restriction covers a resume that re-decodes; a stage with
57
+ durable output is reused, not re-decoded. Owner runs without an input root stay
58
+ unrestricted.
59
+ - **Atomic attempt ownership.** A worker pins the attempt generation it claimed;
60
+ every progress write, checkpoint write, and terminal finalize is guarded on it,
61
+ so a stale run cannot overwrite a newer attempt's label or verdict. The reopen
62
+ of a terminal row for resume is an atomic claim pinned to the observed attempt —
63
+ two callers racing one job id cannot both reopen it.
64
+ - **Atomic worker startup and cancellation.** Worker-thread startup is atomic, and
65
+ the completion/cancellation race is decided in one place against the owned
66
+ attempt: the row becomes CANCELLED only if that generation carries an accepted
67
+ cancellation, DONE otherwise, so a finished DONE row can never still carry an
68
+ accepted cancel.
69
+ - **DOCX identity.** Completed-output reuse for DOCX compares the archive bytes
70
+ with only the local and central ZIP time/date pairs blanked; every other byte
71
+ (member names and order, compression, flags, extra fields, comments, compressed
72
+ content) must be equal, and the comparison never inflates a member.
73
+ - **Completed-stage reuse.** A run leaves a durable per-stage marker for each
74
+ expensive stage it finishes; resume reuses only stages finished for the
75
+ **requested configuration**, and a legacy coarse `postprocess` marker is honoured
76
+ only for the optional stages that record actually requested. The rule keys on
77
+ the marker, never on transcript metadata.
78
+ - **Aggregate export preflight.** The whole requested format set is validated
79
+ (unsupported/duplicate names, then every dependency) and the aggregate rendered
80
+ bytes are bounded **before any destination file is written**, for every surface
81
+ that publishes output.
82
+ - **Translation completeness.** The translator's result length is checked against
83
+ its input and every result is validated **before mutating any segment**: a
84
+ blank result for a non-blank segment raises and leaves the transcript exactly as
85
+ it was, rather than half-writing a set.
86
+ - **Per-item batch validation.** Each batch entry is constructed and admitted
87
+ independently (HTTP and MCP): a non-object entry, a non-string/empty source, an
88
+ unsupported format, or an unusable engine is **that** entry's error, carrying its
89
+ original zero-based `index` and best-effort `source`, while the other entries
90
+ still queue, in submitted order. There is no whole-batch engine preflight.
91
+ - **Terminal feedback.** A non-quiet CLI run streams live stage progress to
92
+ **stderr, flushed** (stdout stays clean for `--stdout`); quiet suppresses it.
93
+ Batch prints each item's result as it finishes. Job status exposes the recorded
94
+ stage.
95
+
96
+ ### Documentation corrections (DOC-001 – DOC-006)
97
+
98
+ - **DOC-001** — the user manual now states that canonical saved/exported JSON
99
+ retains hidden segments and is an archive, not a redaction mechanism, and adds
100
+ a worked safe-publication-copy example that filters the copy without mutating
101
+ the stored transcript.
102
+ - **DOC-002** — the adapter guide and manual now distinguish response shapes:
103
+ HTTP JSON returns a `transcript` object with page counts but no `next`, MCP
104
+ returns a JSON string under `content` and adds a `next` instruction, and HTTP
105
+ text reads carry no paging metadata. Time selection is described as
106
+ overlapping rather than clipped.
107
+ - **DOC-003** — "same options" is replaced with a per-surface parameter matrix
108
+ (HTTP `formats` array vs MCP comma-separated string; HTTP batch options are
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
111
+ `sources` is documented as a list of **strings**: a non-string entry (including
112
+ a nested object or a number) is that entry's own error, not a whole-call
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.
120
+ - **DOC-004** — a full production settings reference (names, defaults, units,
121
+ required fields) is added to the adapter guide, and the production refusal of
122
+ browser-cookie submissions is stated across the adapter guide, the sources
123
+ guide, and `SECURITY.md`, including resume.
124
+ - **DOC-005** — the install guide chooses the diarization install path (preserve
125
+ a working ROCm stack vs fresh CPU) **before** the first pip command, and the
126
+ manual's ROCm deep link now resolves to the actual heading.
127
+ - **DOC-006** — new [architecture and lifecycle guide](docs/architecture.md)
128
+ maps the core/CLI/MCP/HTTP doors, single-owning-process SQLite, startup
129
+ recovery via the HTTP/MCP lifespans and the lazy executor, checkpoint reuse,
130
+ and output publication; the adapter guide's "reaped at startup" wording is
131
+ qualified to match. Linked from the README and `CONTRIBUTING.md`.
132
+
133
+ The production settings reference lives in
134
+ [docs/adapters.md](docs/adapters.md#production-settings-reference).
135
+
136
+ ### Deferred audit follow-ups — later fixes
137
+
138
+ On 2026-10-01, the owner explicitly deferred the following findings from the
139
+ full five-role audit. They remain open; they are not completed fixes, new release
140
+ claims, or additions to the current core-bug repair goal. No release date is set.
141
+
142
+ See [Later fixes in the roadmap](docs/roadmap.md#later-fixes--2026-10-01-audit)
143
+ for the complete list and completion criteria. The current repair goal still
144
+ covers decoder confinement, job lifecycle, publication/resume, translation and
145
+ batch correctness, major terminal guidance/documentation, and its final gate.
146
+
147
+ This changelog starts with the v0.1.8 release notes, followed by the v0.1.7
148
+ release notes; published historical
149
+ release notes remain on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases).
@@ -55,6 +55,13 @@ build of torch; see [docs/install.md](docs/install.md) for the torch-pinning tra
55
55
  Most tests do **not** need a GPU or a model download. If you add one that does,
56
56
  mark it so it can be skipped in CI.
57
57
 
58
+ ## How the pieces fit together
59
+
60
+ [docs/architecture.md](docs/architecture.md) maps the core, CLI, MCP, and HTTP
61
+ doors, the single-owning-process job lifecycle (including startup recovery),
62
+ checkpoint reuse, and output publication. Read it before changing submission,
63
+ executor, runner, or checkpoint behaviour.
64
+
58
65
  ## Pull requests
59
66
 
60
67
  - Keep the change scoped; state what it fixes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: textflowkit
3
- Version: 0.1.6
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.6](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6).**
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
@@ -140,6 +140,9 @@ the core; the interfaces are thin.
140
140
  Because the core owns the pipeline, adding a door is cheap — and adding a platform
141
141
  means writing one source adapter, not another tool.
142
142
 
143
+ For the job lifecycle, process ownership, checkpoints, and output publication,
144
+ see [docs/architecture.md](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md).
145
+
143
146
  ## Recognized sources
144
147
 
145
148
  YouTube · TikTok · Facebook · Instagram · Vimeo · Twitch · Bilibili · Rumble ·
@@ -216,7 +219,7 @@ to preserve it.
216
219
 
217
220
  The same version's wheel and source archive are also on the
218
221
  [GitHub release page](https://github.com/scottconverse/textflowkit/releases/latest).
219
- Starting with the next release, every release published by this project's release
222
+ Starting with v0.1.6, every release published by this project's release
220
223
  workflow carries a `SHA256SUMS` asset listing the SHA-256 hash of each wheel and
221
224
  source archive it contains, so you can check a download with
222
225
  `sha256sum -c SHA256SUMS`. Releases published before that change, v0.1.5
@@ -335,21 +338,43 @@ web frontend share the job contract without blocking a request.
335
338
 
336
339
  ## Status
337
340
 
338
- **v0.1.6 release.** Core, CLI, MCP, and HTTP have automated
339
- coverage. This release is the post-v0.1.5 review repair set: security hardening
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
+
350
+ The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
340
351
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
341
352
  output-file publication, job and checkpoint storage corrections, an opt-in
342
353
  faster-whisper engine, a container example, and fail-closed release guards for
343
354
  tagged versions, README claims, and exact-commit CI.
344
355
  The v0.1.5 release added a speech-bearing self-test, full-media duration,
345
- retained word timings, optional PDF fonts, and tokenless PyPI publishing, and its
346
- live evidence is the only live evidence recorded on this page.
347
- Windows-native ROCm and a local Windows YouTube run on the v0.1.5 release commit were verified.
348
- That run's receipt records the clip, timestamps, and hashes; the checklist's
349
- recognized-speech assertion was added after it, so treat it as shape evidence for
350
- that commit rather than proof of what was said.
356
+ retained word timings, optional PDF fonts, and tokenless PyPI publishing.
357
+ Windows-native ROCm and a local Windows YouTube run on the v0.1.5 release commit
358
+ were verified. That run's receipt records the clip, timestamps, and hashes; the
359
+ checklist's recognized-speech assertion was added after it, so treat it as shape
360
+ evidence for that commit rather than proof of what was said.
361
+ The v0.1.6 release is public: the
362
+ [v0.1.6 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6)
363
+ and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
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.8.
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.8, whose separate release evidence is not supplied by these historical paragraphs.
368
+ Merged-main CI passed 16/16 on the tagged commit; the published wheel and
369
+ sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
370
+ Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
371
+ passed `doctor`, `selftest`, and a tiny CPU transcription to JSON and PDF. A
372
+ local native-Windows YouTube run on the same commit returned 3 timestamped
373
+ segments and a whole-word `elephants` match; its receipt is stored outside this
374
+ repository. One clip is not proof of the other 12 recognized platforms or of
375
+ general accuracy.
351
376
  The GitHub-hosted YouTube attempt was blocked by a bot challenge, so hosted
352
- live transcription is not verified. No v0.1.6 tag, upload, or live run is verified here.
377
+ live transcription is not verified.
353
378
  This does not imply that all 13 platforms or every harness workflow has been
354
379
  tested end to end. See the [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
355
380
  and [roadmap](https://github.com/scottconverse/textflowkit/blob/main/docs/roadmap.md).
@@ -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.6](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6).**
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
@@ -83,6 +83,9 @@ the core; the interfaces are thin.
83
83
  Because the core owns the pipeline, adding a door is cheap — and adding a platform
84
84
  means writing one source adapter, not another tool.
85
85
 
86
+ For the job lifecycle, process ownership, checkpoints, and output publication,
87
+ see [docs/architecture.md](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md).
88
+
86
89
  ## Recognized sources
87
90
 
88
91
  YouTube · TikTok · Facebook · Instagram · Vimeo · Twitch · Bilibili · Rumble ·
@@ -159,7 +162,7 @@ to preserve it.
159
162
 
160
163
  The same version's wheel and source archive are also on the
161
164
  [GitHub release page](https://github.com/scottconverse/textflowkit/releases/latest).
162
- Starting with the next release, every release published by this project's release
165
+ Starting with v0.1.6, every release published by this project's release
163
166
  workflow carries a `SHA256SUMS` asset listing the SHA-256 hash of each wheel and
164
167
  source archive it contains, so you can check a download with
165
168
  `sha256sum -c SHA256SUMS`. Releases published before that change, v0.1.5
@@ -278,21 +281,43 @@ web frontend share the job contract without blocking a request.
278
281
 
279
282
  ## Status
280
283
 
281
- **v0.1.6 release.** Core, CLI, MCP, and HTTP have automated
282
- coverage. This release is the post-v0.1.5 review repair set: security hardening
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
+
293
+ The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
283
294
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
284
295
  output-file publication, job and checkpoint storage corrections, an opt-in
285
296
  faster-whisper engine, a container example, and fail-closed release guards for
286
297
  tagged versions, README claims, and exact-commit CI.
287
298
  The v0.1.5 release added a speech-bearing self-test, full-media duration,
288
- retained word timings, optional PDF fonts, and tokenless PyPI publishing, and its
289
- live evidence is the only live evidence recorded on this page.
290
- Windows-native ROCm and a local Windows YouTube run on the v0.1.5 release commit were verified.
291
- That run's receipt records the clip, timestamps, and hashes; the checklist's
292
- recognized-speech assertion was added after it, so treat it as shape evidence for
293
- that commit rather than proof of what was said.
299
+ retained word timings, optional PDF fonts, and tokenless PyPI publishing.
300
+ Windows-native ROCm and a local Windows YouTube run on the v0.1.5 release commit
301
+ were verified. That run's receipt records the clip, timestamps, and hashes; the
302
+ checklist's recognized-speech assertion was added after it, so treat it as shape
303
+ evidence for that commit rather than proof of what was said.
304
+ The v0.1.6 release is public: the
305
+ [v0.1.6 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6)
306
+ and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
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.8.
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.8, whose separate release evidence is not supplied by these historical paragraphs.
311
+ Merged-main CI passed 16/16 on the tagged commit; the published wheel and
312
+ sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
313
+ Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
314
+ passed `doctor`, `selftest`, and a tiny CPU transcription to JSON and PDF. A
315
+ local native-Windows YouTube run on the same commit returned 3 timestamped
316
+ segments and a whole-word `elephants` match; its receipt is stored outside this
317
+ repository. One clip is not proof of the other 12 recognized platforms or of
318
+ general accuracy.
294
319
  The GitHub-hosted YouTube attempt was blocked by a bot challenge, so hosted
295
- live transcription is not verified. No v0.1.6 tag, upload, or live run is verified here.
320
+ live transcription is not verified.
296
321
  This does not imply that all 13 platforms or every harness workflow has been
297
322
  tested end to end. See the [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
298
323
  and [roadmap](https://github.com/scottconverse/textflowkit/blob/main/docs/roadmap.md).
@@ -50,10 +50,53 @@ user accounts or multi-tenancy.
50
50
  `TEXTFLOWKIT_OUTPUT_ROOT` to confine less-trusted callers. With those roots
51
51
  set, paths outside them (including `..` and symlink escapes) are rejected.
52
52
  See `resolve_output_dir` and `resolve_input_path` in
53
- `src/textflowkit/core/paths.py`. Confined local input is copied from a
54
- path-verified open handle into isolated scratch before ffmpeg. Output paths
55
- are rechecked at file publication. Keep configured roots non-writable by
56
- untrusted local users to prevent races.
53
+ `src/textflowkit/core/paths.py`. Output paths are rechecked at file
54
+ publication. Keep configured roots non-writable by untrusted local users to
55
+ prevent races.
56
+
57
+ - **The decoder boundary for confined input.** A local file can be a
58
+ *reference* to other files rather than media itself: an HLS/M3U playlist, an
59
+ ffmpeg concat script, a DASH manifest. The demuxer opens every path such a
60
+ file names. Confined local input is handle-verified and copied into isolated
61
+ scratch, but only the top-level file is copied, so a playlist inside the root
62
+ can name a file outside it — an absolute reference survives the copy
63
+ unchanged, and a relative one resolves against the scratch directory, where
64
+ `..` walks out. When an input root is set, every confined input is therefore
65
+ decoded under FFmpeg's `-format_whitelist`, listing only the demuxers this
66
+ product supports as *self-contained* starting formats:
67
+
68
+ wav, mp3, mov,mp4,m4a,3gp,3g2,mj2, matroska,webm, ogg, flac, aac
69
+
70
+ These are FFmpeg's own demuxer names (`ffmpeg -demuxers`), not the extension
71
+ list. A file whose content is not one of them — including a playlist or
72
+ manifest however it is named — is refused by FFmpeg itself, before any
73
+ reference is followed. The same restriction is applied to the `ffprobe`
74
+ duration check, which otherwise opens the staged file the same way.
75
+
76
+ This is the guarantee, stated exactly:
77
+
78
+ - **Confined local input cannot make FFmpeg open another file.** The demuxer
79
+ that would follow a reference is not selectable, so there is no reference to
80
+ check and none is followed. This is a decoder-format restriction, not a
81
+ filesystem sandbox.
82
+ - **It applies only to confined input** (an input root set) and only to the
83
+ media decode. Without an input root there is no boundary to protect, and the
84
+ documented workflow decodes what FFmpeg already decoded.
85
+ - **It rests on the demuxer list above.** A file that this build classifies as
86
+ one of those demuxers, but which in another FFmpeg build (or through a
87
+ container's own external-data mechanism) opens other files, is not covered.
88
+ The `-format_whitelist` names are checked against `ffmpeg -demuxers`; a
89
+ build whose demuxers differ is not verified here.
90
+ - **It is not a sandbox of FFmpeg or yt-dlp at large** (see "Not defended").
91
+
92
+ - **Leading-byte manifest refusal (defense in depth).** On top of the decoder
93
+ whitelist, a confined input whose leading bytes are an HLS/M3U playlist
94
+ (`#EXT`), an MPEG-DASH manifest (`<`), or an ffmpeg concat script
95
+ (`ffconcat`) is refused before the staged copy is written, so a recognised
96
+ manifest leaves nothing behind. This is an extra layer, deliberately *not*
97
+ the boundary: it covers only the signatures in its list, and detection is by
98
+ content rather than extension. The decoder whitelist above is what holds if
99
+ this check is widened, narrowed, or removed.
57
100
 
58
101
  ### Not defended (by design — read before deploying)
59
102
 
@@ -64,10 +107,19 @@ user accounts or multi-tenancy.
64
107
  Streamable-HTTP MCP is separate and not covered by this Bearer middleware.
65
108
  - **No sandboxing of `ffmpeg` / `yt-dlp`.** They run as your user on input you
66
109
  supply. Media is untrusted data; treat a malformed file as you would any
67
- untrusted input to those tools.
110
+ untrusted input to those tools. The confined-input decoder boundary above
111
+ limits which *formats* a confined input may be, and so which files the demuxer
112
+ can open; it does not confine the decoder process, its codecs, or anything
113
+ else on the system.
68
114
  - **Cookies are passed through, not stored.** `--cookies-from-browser` hands
69
115
  browser cookies to `yt-dlp` for content you are authorised to access. Using it
70
116
  to reach content you are not authorised to access is outside the intended use.
117
+ Browser-cookie input is a **developer/owner-profile** feature: while
118
+ `TEXTFLOWKIT_PROFILE=production`, every submission carrying the option is
119
+ refused before a job is written — on the CLI, MCP, and HTTP surfaces, and when
120
+ a saved request is resumed — because the server cannot distinguish an
121
+ owner-run session from a remote caller. See the [production settings
122
+ reference](docs/adapters.md#production-settings-reference).
71
123
  - **Transcript accuracy is not a security property.** Speech-to-text output
72
124
  contains errors and must not be relied on for legal, medical, safety-critical,
73
125
  or evidentiary purposes without human review.