utterplan 0.5.0__tar.gz → 0.6.1__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 (240) hide show
  1. {utterplan-0.5.0 → utterplan-0.6.1}/.github/workflows/python-publish.yml +2 -1
  2. {utterplan-0.5.0 → utterplan-0.6.1}/.github/workflows/tests.yml +90 -4
  3. {utterplan-0.5.0 → utterplan-0.6.1}/AGENTS.md +2 -2
  4. {utterplan-0.5.0 → utterplan-0.6.1}/PKG-INFO +94 -14
  5. utterplan-0.6.1/README.md +279 -0
  6. utterplan-0.6.1/docs/architecture.md +587 -0
  7. {utterplan-0.5.0 → utterplan-0.6.1}/docs/changelog.md +34 -0
  8. utterplan-0.6.1/docs/cli.md +302 -0
  9. utterplan-0.6.1/docs/consumer-guide.md +121 -0
  10. {utterplan-0.5.0 → utterplan-0.6.1}/docs/coordinate-spaces.md +9 -3
  11. utterplan-0.6.1/docs/debugging.md +42 -0
  12. utterplan-0.6.1/docs/format.md +81 -0
  13. {utterplan-0.5.0 → utterplan-0.6.1}/docs/getting-started.md +35 -10
  14. utterplan-0.6.1/docs/index.md +30 -0
  15. {utterplan-0.5.0 → utterplan-0.6.1}/docs/pykokoro-integration.md +6 -5
  16. {utterplan-0.5.0 → utterplan-0.6.1}/docs/python-api.md +74 -16
  17. utterplan-0.6.1/docs/ssmdbook.md +139 -0
  18. {utterplan-0.5.0 → utterplan-0.6.1}/pyproject.toml +2 -1
  19. {utterplan-0.5.0 → utterplan-0.6.1}/tests/compiler_helpers.py +0 -2
  20. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/cases.py +25 -9
  21. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/goldens/multilingual_preparation.json +2 -2
  22. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/goldens/ssmd_break.json +1 -1
  23. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/test_legacy_json_import.py +4 -7
  24. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/test_semantic_migration_regressions.py +37 -2
  25. utterplan-0.6.1/tests/schema_history/README.md +7 -0
  26. utterplan-0.6.1/tests/schema_history/v5/canonical_consumer_contract.toml +35 -0
  27. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_batch.py +1 -1
  28. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_boundaries.py +45 -1
  29. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_cli.py +187 -26
  30. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_cli_batch.py +10 -4
  31. utterplan-0.6.1/tests/test_cli_render_book.py +489 -0
  32. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_cli_ssmdbook.py +153 -3
  33. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_compiler.py +8 -11
  34. utterplan-0.6.1/tests/test_consumer_contract.py +350 -0
  35. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_consumer_readiness.py +24 -23
  36. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_coordinates.py +6 -4
  37. utterplan-0.6.1/tests/test_dependency_architecture.py +41 -0
  38. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_explain.py +73 -19
  39. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_fake_renderer.py +3 -4
  40. utterplan-0.6.1/tests/test_flow_compilation.py +201 -0
  41. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_format.py +11 -11
  42. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_golden.py +12 -11
  43. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_identity_units.py +7 -9
  44. utterplan-0.6.1/tests/test_import_boundaries.py +29 -0
  45. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_linguistics.py +121 -112
  46. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_migration_framework.py +3 -3
  47. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_migration_v4_to_v5.py +36 -2
  48. utterplan-0.6.1/tests/test_pauses.py +146 -0
  49. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_planner.py +123 -18
  50. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_preparation.py +16 -12
  51. utterplan-0.6.1/tests/test_preview.py +161 -0
  52. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_progress.py +1 -0
  53. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_public_identity.py +4 -1
  54. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_renderability.py +1 -1
  55. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_renderability_repair.py +31 -38
  56. utterplan-0.6.1/tests/test_renderplan_batch.py +334 -0
  57. utterplan-0.6.1/tests/test_renderplan_dependency_contract.py +22 -0
  58. utterplan-0.6.1/tests/test_renderplan_export.py +264 -0
  59. utterplan-0.6.1/tests/test_renderplan_replan.py +402 -0
  60. utterplan-0.6.1/tests/test_renderplan_v7_contract.py +523 -0
  61. utterplan-0.6.1/tests/test_replacement_provenance.py +228 -0
  62. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_schema_history.py +9 -2
  63. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_schema_registry.py +7 -3
  64. utterplan-0.6.1/tests/test_schema_v6_source.py +122 -0
  65. utterplan-0.6.1/tests/test_schema_v7.py +148 -0
  66. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_segment_topology.py +34 -8
  67. utterplan-0.6.1/tests/test_segmentation_adapter.py +203 -0
  68. utterplan-0.6.1/tests/test_segmentation_recovery.py +162 -0
  69. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_semantic_boundaries.py +0 -1
  70. utterplan-0.6.1/tests/test_sentence_topology.py +361 -0
  71. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_ssmd_09_parser.py +13 -4
  72. utterplan-0.6.1/tests/test_ssmd_substitution_preparation.py +257 -0
  73. utterplan-0.6.1/tests/test_token_topology.py +73 -0
  74. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_toml_format.py +37 -19
  75. utterplan-0.6.1/tests/test_ttsready_preparation_adapter.py +305 -0
  76. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/__init__.py +22 -2
  77. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/_version.py +3 -3
  78. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/batch.py +58 -3
  79. utterplan-0.6.1/utterplan/boundaries.py +51 -0
  80. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/cli.py +669 -46
  81. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/codecs/v5_toml.py +126 -19
  82. utterplan-0.6.1/utterplan/codecs/v6_toml.py +123 -0
  83. utterplan-0.6.1/utterplan/codecs/v7_toml.py +625 -0
  84. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/compiler.py +1 -0
  85. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/config.py +24 -21
  86. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/directives.py +2 -1
  87. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/exceptions.py +3 -4
  88. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/explain.py +40 -5
  89. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/flow_projection.py +144 -18
  90. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/format.py +3 -3
  91. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/hashing.py +31 -7
  92. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/linguistics.py +1 -1
  93. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migration.py +2 -0
  94. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migrations/__init__.py +4 -0
  95. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migrations/v4_to_v5.py +77 -2
  96. utterplan-0.6.1/utterplan/migrations/v5_to_v6.py +48 -0
  97. utterplan-0.6.1/utterplan/migrations/v6_to_v7.py +34 -0
  98. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/model.py +440 -66
  99. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/parsers.py +105 -1
  100. utterplan-0.6.1/utterplan/pauses.py +80 -0
  101. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/planner.py +883 -252
  102. utterplan-0.6.1/utterplan/preparation.py +665 -0
  103. utterplan-0.6.1/utterplan/preview.py +257 -0
  104. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/renderability.py +9 -25
  105. utterplan-0.6.1/utterplan/renderplan_batch.py +471 -0
  106. utterplan-0.6.1/utterplan/renderplan_export.py +160 -0
  107. utterplan-0.6.1/utterplan/renderplan_grouping.py +729 -0
  108. utterplan-0.6.1/utterplan/renderplan_projection.py +369 -0
  109. utterplan-0.6.1/utterplan/renderplan_replan.py +273 -0
  110. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/schema_registry.py +1 -1
  111. utterplan-0.6.1/utterplan/segmentation.py +959 -0
  112. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/serialization.py +6 -9
  113. utterplan-0.6.1/utterplan/stages/preparation/ssmd.py +3 -0
  114. utterplan-0.6.1/utterplan/stages/preparation/ttsready.py +3 -0
  115. utterplan-0.6.1/utterplan/token_topology.py +102 -0
  116. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/toml_codec.py +19 -23
  117. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/units.py +4 -5
  118. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/versioning.py +5 -3
  119. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/PKG-INFO +94 -14
  120. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/SOURCES.txt +33 -1
  121. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/requires.txt +2 -1
  122. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/scm_file_list.json +95 -1
  123. utterplan-0.6.1/utterplan.egg-info/scm_version.json +8 -0
  124. utterplan-0.5.0/README.md +0 -200
  125. utterplan-0.5.0/docs/architecture.md +0 -63
  126. utterplan-0.5.0/docs/cli.md +0 -204
  127. utterplan-0.5.0/docs/consumer-guide.md +0 -92
  128. utterplan-0.5.0/docs/debugging.md +0 -28
  129. utterplan-0.5.0/docs/format.md +0 -62
  130. utterplan-0.5.0/docs/index.md +0 -31
  131. utterplan-0.5.0/docs/ssmdbook.md +0 -72
  132. utterplan-0.5.0/tests/schema_history/README.md +0 -9
  133. utterplan-0.5.0/tests/test_consumer_contract.py +0 -182
  134. utterplan-0.5.0/tests/test_flow_compilation.py +0 -97
  135. utterplan-0.5.0/tests/test_import_boundaries.py +0 -16
  136. utterplan-0.5.0/tests/test_pauses.py +0 -213
  137. utterplan-0.5.0/utterplan/pauses.py +0 -108
  138. utterplan-0.5.0/utterplan/preparation.py +0 -263
  139. utterplan-0.5.0/utterplan/stages/preparation/spokenform.py +0 -3
  140. utterplan-0.5.0/utterplan.egg-info/scm_version.json +0 -8
  141. {utterplan-0.5.0 → utterplan-0.6.1}/.codecrate.toml +0 -0
  142. {utterplan-0.5.0 → utterplan-0.6.1}/.github/workflows/codecov.yml +0 -0
  143. {utterplan-0.5.0 → utterplan-0.6.1}/.github/workflows/pre-commit.yml +0 -0
  144. {utterplan-0.5.0 → utterplan-0.6.1}/.gitignore +0 -0
  145. {utterplan-0.5.0 → utterplan-0.6.1}/.pre-commit-config.yaml +0 -0
  146. {utterplan-0.5.0 → utterplan-0.6.1}/LICENSE +0 -0
  147. {utterplan-0.5.0 → utterplan-0.6.1}/MANIFEST.in +0 -0
  148. {utterplan-0.5.0 → utterplan-0.6.1}/NOTICE +0 -0
  149. {utterplan-0.5.0 → utterplan-0.6.1}/docs/Makefile +0 -0
  150. {utterplan-0.5.0 → utterplan-0.6.1}/docs/conf.py +0 -0
  151. {utterplan-0.5.0 → utterplan-0.6.1}/docs/make.bat +0 -0
  152. {utterplan-0.5.0 → utterplan-0.6.1}/docs/phase-a-completion.md +0 -0
  153. {utterplan-0.5.0 → utterplan-0.6.1}/docs/requirements.txt +0 -0
  154. {utterplan-0.5.0 → utterplan-0.6.1}/examples/basic.py +0 -0
  155. {utterplan-0.5.0 → utterplan-0.6.1}/examples/chapter.ssmd +0 -0
  156. {utterplan-0.5.0 → utterplan-0.6.1}/examples/compile_file.py +0 -0
  157. {utterplan-0.5.0 → utterplan-0.6.1}/examples/inspect_plan.py +0 -0
  158. {utterplan-0.5.0 → utterplan-0.6.1}/examples/multilingual.py +0 -0
  159. {utterplan-0.5.0 → utterplan-0.6.1}/examples/ssmd_example.py +0 -0
  160. {utterplan-0.5.0 → utterplan-0.6.1}/setup.cfg +0 -0
  161. {utterplan-0.5.0 → utterplan-0.6.1}/spec/schemas/v1.schema.json +0 -0
  162. {utterplan-0.5.0 → utterplan-0.6.1}/spec/schemas/v2.schema.json +0 -0
  163. {utterplan-0.5.0 → utterplan-0.6.1}/spec/schemas/v3.schema.json +0 -0
  164. {utterplan-0.5.0 → utterplan-0.6.1}/spec/schemas/v4.schema.json +0 -0
  165. {utterplan-0.5.0 → utterplan-0.6.1}/spec/utterplan.schema.json +0 -0
  166. {utterplan-0.5.0 → utterplan-0.6.1}/tests/__init__.py +0 -0
  167. {utterplan-0.5.0 → utterplan-0.6.1}/tests/fixtures/canonical_consumer_contract.expected.toml +0 -0
  168. {utterplan-0.5.0 → utterplan-0.6.1}/tests/fixtures/canonical_consumer_contract.ssmd +0 -0
  169. {utterplan-0.5.0 → utterplan-0.6.1}/tests/fixtures/ssmd_09_comprehensive.ssmd +0 -0
  170. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/basic_en.utterplan.toml +0 -0
  171. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/directives.utterplan.toml +0 -0
  172. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/markers.utterplan.toml +0 -0
  173. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/multilingual.utterplan.toml +0 -0
  174. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/parenthetical.utterplan.toml +0 -0
  175. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/spokenform_offsets.utterplan.toml +0 -0
  176. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/ssmd_09_comprehensive.utterplan.toml +0 -0
  177. {utterplan-0.5.0 → utterplan-0.6.1}/tests/golden/ssmd_breaks.utterplan.toml +0 -0
  178. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/__init__.py +0 -0
  179. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/basic_en.utterplan.json +0 -0
  180. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/directives.utterplan.json +0 -0
  181. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/markers.utterplan.json +0 -0
  182. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/multilingual.utterplan.json +0 -0
  183. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/parenthetical.utterplan.json +0 -0
  184. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/spokenform_offsets.utterplan.json +0 -0
  185. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/ssmd_09_comprehensive.utterplan.json +0 -0
  186. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/fixtures/v4/ssmd_breaks.utterplan.json +0 -0
  187. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/goldens/spokenform_numbers.json +0 -0
  188. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/goldens/ssmd_language_detection.json +0 -0
  189. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/goldens/ssmd_marker.json +0 -0
  190. {utterplan-0.5.0 → utterplan-0.6.1}/tests/migration/goldens/ssmd_pronunciation.json +0 -0
  191. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/basic.json +0 -0
  192. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/directives.json +0 -0
  193. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/markers.json +0 -0
  194. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/multilingual.json +0 -0
  195. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/parenthetical.json +0 -0
  196. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/spokenform_offsets.json +0 -0
  197. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v1/ssmd_breaks.json +0 -0
  198. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v2/directives.json +0 -0
  199. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v3/multilingual.json +0 -0
  200. {utterplan-0.5.0 → utterplan-0.6.1}/tests/schema_history/v3/ssmd_09_comprehensive.json +0 -0
  201. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_atomic_io.py +0 -0
  202. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_directives.py +0 -0
  203. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_language.py +0 -0
  204. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_migration_v3_to_v4.py +0 -0
  205. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_schema_strict.py +0 -0
  206. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_ssmdbook.py +0 -0
  207. {utterplan-0.5.0 → utterplan-0.6.1}/tests/test_version.py +0 -0
  208. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/__main__.py +0 -0
  209. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/atomic_io.py +0 -0
  210. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/attempt_serialization.py +0 -0
  211. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/attempts.py +0 -0
  212. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/codecs/__init__.py +0 -0
  213. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/codecs/v4_toml.py +0 -0
  214. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/diagnostics.py +0 -0
  215. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/language.py +0 -0
  216. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migrations/registry.py +0 -0
  217. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migrations/v1_to_v2.py +0 -0
  218. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migrations/v2_to_v3.py +0 -0
  219. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/migrations/v3_to_v4.py +0 -0
  220. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/progress.py +0 -0
  221. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/py.typed +0 -0
  222. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/schemas/v1.schema.json +0 -0
  223. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/schemas/v2.schema.json +0 -0
  224. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/schemas/v3.schema.json +0 -0
  225. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/schemas/v4.schema.json +0 -0
  226. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/spacy_models.py +0 -0
  227. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/spans.py +0 -0
  228. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/ssmdbook.py +0 -0
  229. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/__init__.py +0 -0
  230. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/document/__init__.py +0 -0
  231. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/document/plain.py +0 -0
  232. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/document/ssmd.py +0 -0
  233. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/preparation/__init__.py +0 -0
  234. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/preparation/identity.py +0 -0
  235. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/stages/protocols.py +0 -0
  236. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/trace_codec.py +0 -0
  237. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan/utterplan.schema.json +0 -0
  238. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/dependency_links.txt +0 -0
  239. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/entry_points.txt +0 -0
  240. {utterplan-0.5.0 → utterplan-0.6.1}/utterplan.egg-info/top_level.txt +0 -0
@@ -81,7 +81,8 @@ jobs:
81
81
  ]
82
82
  assert has_range(requirements, "phrasplit", "0.3.9", "0.4")
83
83
  assert has_range(requirements, "ssmd", "0.9.3", "0.10")
84
- assert has_range(requirements, "spokenform", "0.4.3", "0.5")
84
+ assert has_range(requirements, "ttsready", "0.2.3", "0.3")
85
+ assert has_range(requirements, "renderplan", "0.1.1", "0.2")
85
86
  names = set(archive.namelist())
86
87
  expected = {
87
88
  "utterplan/py.typed",
@@ -49,7 +49,9 @@ jobs:
49
49
  python -m pip install \
50
50
  "phrasplit==0.3.9" \
51
51
  "ssmd==0.9.3" \
52
- "spokenform==0.4.3" \
52
+ "ttsready==0.2.3" \
53
+ "spokenform==0.4.5" \
54
+ "renderplan==0.1.1" \
53
55
  "typing_extensions==4.0.0" \
54
56
  "tomlkit==0.13.0"
55
57
  python -m pip install --no-deps -e .
@@ -58,6 +60,26 @@ jobs:
58
60
  - name: Run lower-bound tests
59
61
  run: python -m pytest -q
60
62
 
63
+ latest-renderplan:
64
+ runs-on: ubuntu-latest
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+ - name: Set up Python
68
+ uses: actions/setup-python@v5
69
+ with:
70
+ python-version: "3.13"
71
+ - name: Install latest supported dependencies
72
+ run: |
73
+ python -m pip install --upgrade pip
74
+ python -m pip install -e ".[dev]"
75
+ - name: Run RenderPlan integration tests
76
+ run: >-
77
+ python -m pytest -q
78
+ tests/test_renderplan_batch.py
79
+ tests/test_renderplan_export.py
80
+ tests/test_renderplan_replan.py
81
+ tests/test_cli_render_book.py
82
+ tests/test_renderplan_dependency_contract.py
61
83
  package-smoke:
62
84
  runs-on: ubuntu-latest
63
85
  steps:
@@ -97,7 +119,8 @@ jobs:
97
119
  expected_ranges = {
98
120
  "phrasplit": ("0.3.9", "0.4"),
99
121
  "ssmd": ("0.9.3", "0.10"),
100
- "spokenform": ("0.4.3", "0.5"),
122
+ "ttsready": ("0.2.3", "0.3"),
123
+ "renderplan": ("0.1.1", "0.2"),
101
124
  }
102
125
 
103
126
  def check_metadata(contents):
@@ -146,8 +169,11 @@ jobs:
146
169
  "$RUNNER_TEMP"/dist/utterplan-*.whl
147
170
  - name: Run installed smoke checks
148
171
  run: |
172
+ cd "$RUNNER_TEMP"
149
173
  "$RUNNER_TEMP/utterplan-wheel-venv/bin/python" -c \
150
174
  "import importlib.metadata as m; import utterplan; assert m.version('utterplan') == utterplan.__version__"
175
+ "$RUNNER_TEMP/utterplan-wheel-venv/bin/python" -c \
176
+ "import utterplan.renderplan_batch; from renderplan import RenderPlanSet"
151
177
  "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" --version
152
178
  cat > "$RUNNER_TEMP/test.ssmd.md" <<'EOF'
153
179
  ---
@@ -163,6 +189,61 @@ jobs:
163
189
  > "$RUNNER_TEMP/test.utterplan.toml"
164
190
  "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" validate \
165
191
  "$RUNNER_TEMP/test.utterplan.toml"
192
+ BOOK="$RUNNER_TEMP/render-book.smoke.ssmdbook"
193
+ mkdir -p "$BOOK/chapters" "$BOOK/utterplan"
194
+ cat > "$BOOK/chapters/chapter-0001.ssmd.md" <<'EOF'
195
+ ---
196
+ ssmd_version: "0.9"
197
+ language: en-US
198
+ ---
199
+ Installed wheel render-book smoke.
200
+ EOF
201
+ cat > "$BOOK/manifest.json" <<'EOF'
202
+ {
203
+ "format": "ssmdconvert.book",
204
+ "schema_version": 1,
205
+ "ssmd_version": "0.9",
206
+ "chapters": [
207
+ {
208
+ "id": "chapter-0001",
209
+ "source_number": 1,
210
+ "path": "chapters/chapter-0001.ssmd.md",
211
+ "title": "Chapter 1"
212
+ }
213
+ ]
214
+ }
215
+ EOF
216
+ "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" compile \
217
+ "$BOOK/chapters/chapter-0001.ssmd.md" --lang en-us --text-preparation identity \
218
+ -o "$BOOK/utterplan/chapter-0001.utterplan.toml"
219
+ "$RUNNER_TEMP/utterplan-wheel-venv/bin/python" - "$BOOK/narration.renderprofile.toml" <<'PY'
220
+ import sys
221
+ from pathlib import Path
222
+
223
+ from renderplan import PausePolicy, PlanningPolicy, RenderProfile, RoleProfile
224
+ from renderplan import codec
225
+
226
+ profile = RenderProfile(
227
+ roles={"narrator": RoleProfile(language="en-US", binding={"engine": "smoke"})},
228
+ default_role="narrator",
229
+ planning=PlanningPolicy(split="sentence"),
230
+ pauses=PausePolicy(mode="manual"),
231
+ )
232
+ codec.save_profile(profile, Path(sys.argv[1]))
233
+ PY
234
+ "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" render-book \
235
+ "$BOOK" --profile "$BOOK/narration.renderprofile.toml" --chapters 1
236
+ "$RUNNER_TEMP/utterplan-wheel-venv/bin/python" - "$BOOK" <<'PY'
237
+ import sys
238
+ from pathlib import Path
239
+
240
+ from renderplan import load_renderplan_set
241
+
242
+ book = Path(sys.argv[1])
243
+ set_path = next((book / "renderplan").glob("*/renderplan-set.toml"))
244
+ renderplan_set = load_renderplan_set(set_path)
245
+ assert [chapter.id for chapter in renderplan_set.chapters] == ["chapter-0001"]
246
+ PY
166
247
  cat > "$RUNNER_TEMP/test-0.8.ssmd.md" <<'EOF'
167
248
  ---
168
249
  ssmd_version: "0.8"
@@ -177,8 +258,13 @@ jobs:
177
258
  exit 1
178
259
  fi
179
260
  grep -q "header.version_unsupported" "$RUNNER_TEMP/test-0.8.out"
180
- "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" migrate \
181
- "$GITHUB_WORKSPACE/tests/schema_history/v1/basic.json" --check
261
+ if "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" migrate \
262
+ "$GITHUB_WORKSPACE/tests/schema_history/v1/basic.json" --check > "$RUNNER_TEMP/migration-check.out" 2>&1; then
263
+ cat "$RUNNER_TEMP/migration-check.out"
264
+ echo "Expected historical schema migration to require recompilation" >&2
265
+ exit 1
266
+ fi
267
+ grep -q "migration.recompile_required" "$RUNNER_TEMP/migration-check.out"
182
268
  "$RUNNER_TEMP/utterplan-wheel-venv/bin/utterplan" inspect \
183
269
  "$RUNNER_TEMP/test.utterplan.toml" --segment 0
184
270
 
@@ -1,7 +1,7 @@
1
1
  # UtterPlan development rules
2
2
 
3
3
  1. UtterPlan is engine-independent.
4
- 2. `.utterplan.toml` schema v5 is the current inter-package contract; historical JSON schemas v1–v4 remain migration inputs only.
4
+ 2. `.utterplan.toml` schema v6 is the current inter-package contract; schema-v5 TOML and historical JSON schemas v1–v4 remain sequential migration inputs.
5
5
  3. Python objects may not contain semantic state that cannot survive file roundtrip.
6
6
  4. UtterPlan ends before G2P.
7
7
  5. Core code must not import PyKokoro, PiperSynth, kokorog2p, piperg2p, ONNX Runtime, or audio packages.
@@ -12,7 +12,7 @@
12
12
  10. Schema changes require explicit compatibility review.
13
13
  11. Repository tests must be self-contained and must not require sibling consumer checkouts.
14
14
  12. Consumer-specific compatibility testing belongs in the consumer repository.
15
- 13. Released schema files are immutable and retained under versioned paths; v5 has no historical JSON Schema resource.
15
+ 13. Released schema files are immutable and retained under versioned paths; schemas v5 and v6 have no JSON Schema resources.
16
16
  14. Every supported older schema must retain a sequential migration path to current.
17
17
  15. Migrations operate on serialized plain data and may not call parser, planner, G2P, renderer, or audio stages.
18
18
  16. Schema changes require frozen historical fixture coverage, deterministic migration tests, and package artifact checks.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utterplan
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: Engine-independent text-to-speech planning compiler and interchange format
5
5
  Author-email: Holger Nahrstaedt <nahrstaedt@gmail.com>
6
6
  License: Apache License
@@ -224,9 +224,10 @@ License-File: LICENSE
224
224
  License-File: NOTICE
225
225
  Requires-Dist: phrasplit<0.4,>=0.3.9
226
226
  Requires-Dist: ssmd<0.10,>=0.9.3
227
- Requires-Dist: spokenform<0.5,>=0.4.3
227
+ Requires-Dist: ttsready<0.3,>=0.2.3
228
228
  Requires-Dist: typing_extensions>=4.0
229
229
  Requires-Dist: tomlkit<1,>=0.13
230
+ Requires-Dist: renderplan<0.2,>=0.1.1
230
231
  Provides-Extra: dev
231
232
  Requires-Dist: build; extra == "dev"
232
233
  Requires-Dist: jsonschema; extra == "dev"
@@ -250,9 +251,9 @@ Dynamic: license-file
250
251
 
251
252
  UtterPlan is the canonical, engine-independent semantic compiler from one SSMD
252
253
  document (or explicitly selected plain-text document) to an executable speech
253
- plan. It produces deterministic prepared text, ordered flow units, segment-local
254
- semantics, pause intents, portable document metadata, and typed directives for
255
- independent consumers such as ttsready and Readio. It does not manage or modify book workspaces; `compile-book` reads an SSMDBook manifest only to discover selected source chapters. It stops before G2P, synthesis, and audio.
254
+ plan. It produces deterministic prepared text, paragraph-local flow units, safe boundary opportunities, authored pause intents, and segment-local
255
+ semantics, portable document metadata, and typed directives for
256
+ independent downstream consumers such as Readio, VoiceRender, and other FlowPlan users. Its book commands read the SSMDBook manifest only for chapter discovery, never modify the manifest or source chapters, and write derived plans to separate directories. It uses TTSReady for written-to-spoken preparation and stops before G2P, synthesis, and audio.
256
257
 
257
258
  ## CLI
258
259
 
@@ -282,7 +283,7 @@ Compile independent documents incrementally; every successful plan is saved imme
282
283
  utterplan compile-many examples/*.ssmd --output-dir build/plans
283
284
  ```
284
285
 
285
- This writes one `.utterplan.toml` per source plus an atomically refreshed `compile-report.toml`. Use `--fail-fast` to skip later inputs after a failure, `--force` to replace existing plans, or `--report PATH` to choose another report location.
286
+ This writes one `.utterplan.toml` per source plus an atomically refreshed `compile-report.toml`. Terminal diagnostics default to warnings; `--diagnostics all` also prints informational records, while `--diagnostics none` suppresses non-failure details. Failures are always reported, and the operational report retains every diagnostic. Use `--fail-fast` to skip later inputs after a failure, `--force` to replace existing plans, or `--report PATH` to choose another report location.
286
287
 
287
288
  Compile a selected chapter range directly from an editable SSMDBook workspace:
288
289
 
@@ -292,7 +293,7 @@ cd "Platform Decay - Martha Wells.ssmdbook"
292
293
  utterplan compile-book chapters --chapters 5-17
293
294
  ```
294
295
 
295
- The command reads the workspace manifest to discover chapters and writes independent v5 plans and a report under `utterplan/`:
296
+ The command reads the workspace manifest to discover chapters and writes independent v7 plans and a report under `utterplan/`:
296
297
 
297
298
  ```text
298
299
  utterplan/
@@ -303,7 +304,59 @@ utterplan/
303
304
  └── chapter-0017.utterplan.toml
304
305
  ```
305
306
 
306
- Omit `--chapters` to compile all manifest chapters. Use comma-separated source-number selections for non-contiguous ranges, for example `utterplan compile-book chapters --chapters 1,3-5`. The book root itself is also accepted as input; see the [SSMDBook guide](docs/ssmdbook.md).
307
+ Omit `--chapters` to compile all manifest chapters. Use comma-separated source-number selections for non-contiguous ranges, for example `utterplan compile-book chapters --chapters 1,3-5`. `--diagnostics warnings|all|none` defaults to warnings; failures are always shown and reports retain complete diagnostics. The book root itself is also accepted as input; see the [SSMDBook guide](docs/ssmdbook.md).
308
+
309
+ ## From an SSMDBook to RenderPlans
310
+
311
+ UtterPlan keeps one canonical semantic plan per chapter. A RenderProfile is a
312
+ separate renderer configuration; create and validate it with VoiceRender, then
313
+ project the selected saved plans into a profile-specific RenderPlan collection:
314
+
315
+ ```bash
316
+ voicerender profile create \
317
+ --engine piper \
318
+ --target en_US-amy-medium \
319
+ --language en-US \
320
+ --offline \
321
+ --output narration.renderprofile.toml
322
+ voicerender profile validate narration.renderprofile.toml
323
+
324
+ utterplan compile-book . --chapters 5-17
325
+ utterplan render-book . --chapters 5-17 --profile narration.renderprofile.toml
326
+ ```
327
+
328
+ The book's `utterplan/` directory remains the shared semantic source. The new
329
+ command writes independent renderer-specific plans into a deterministic variant:
330
+
331
+ ```text
332
+ renderplan/
333
+ └── narration--<profile-fingerprint>/
334
+ ├── renderprofile.toml
335
+ ├── renderplan-set.toml
336
+ ├── render-report.toml
337
+ ├── chapter-0005.renderplan.toml
338
+ └── ...
339
+ ```
340
+
341
+ The variant label comes from the profile filename by default; its suffix is the
342
+ first 12 hexadecimal characters of the canonical RenderProfile `profile_id`.
343
+ Changing identity-bearing profile semantics selects a different directory, so
344
+ multiple voices or planning policies can coexist without replacing one another.
345
+ Each variant snapshots the canonical profile; `renderplan-set.toml` records its
346
+ full identity, source profile reference, and chapter plan IDs. `--variant` changes
347
+ the human label without removing the fingerprint, and `--output-root` changes the
348
+ collection root while retaining the automatic variant directory.
349
+
350
+ `render-book` consumes only the existing schema-v7 UtterPlans and shared
351
+ RenderProfile/RenderPlan contract. VoiceRender is not required to run it: VoiceRender
352
+ creates or refreshes engine-backed profiles, then preflights and renders the
353
+ resulting RenderPlans. The single-file `utterplan render-plan` command remains the
354
+ low-level projection and deterministic replan primitive.
355
+
356
+ Related packages: [RenderPlan](https://pypi.org/project/renderplan/) defines the
357
+ shared profile and plan contracts; [VoiceRender](https://pypi.org/project/voicerender/)
358
+ creates engine-backed profiles and performs preflight and synthesis.
359
+
307
360
  The CLI also provides:
308
361
 
309
362
  ```bash
@@ -314,10 +367,14 @@ utterplan inspect chapter.utterplan.toml --boundaries --tokens
314
367
  utterplan explain chapter.utterplan.toml --trace chapter.trace.toml
315
368
  utterplan inspect-trace chapter.trace.toml --preparation --boundaries
316
369
  utterplan inspect-attempt chapter.attempt.toml --issues
370
+ utterplan preview chapter.utterplan.toml --segments 58-62 --chunk-by clause --pause-policy automatic
371
+ utterplan render-plan chapter.utterplan.toml --profile voice.renderprofile.toml -o chapter.renderplan.toml --explain
317
372
  ```
318
373
 
319
374
  `explain` presents executable flow as a human-readable speech plan. Use `compile --trace PATH` to retain optional compiler provenance; `explain --trace PATH` and `inspect-trace` read that sidecar separately from the plan.
320
375
 
376
+ `render-plan` projects only current schema-v7 FlowPlans into the shared RenderPlan contract. Replanning requires both the exact previous RenderPlan and its matching preflight report; UtterPlan refines only failed speech occurrences and does not calculate renderer-specific metrics.
377
+
321
378
  `inspect-attempt` reads the separate `utterplan.planning-attempt.v1` TOML artifact,
322
379
  not a canonical plan. Use it to review renderability issues and safe repair
323
380
  assessments on blocked planning outcomes; such candidates remain inspect-only drafts.
@@ -337,18 +394,18 @@ command:
337
394
  ssmd migrate old.ssmd --to 0.9
338
395
  ```
339
396
 
340
- `utterplan migrate` explicitly imports supported historical `.utterplan.json` plans and writes current TOML. It does not migrate SSMD source.
397
+ `utterplan migrate` handles only serialized plan data; v6-to-v7 migration refuses with `migration.recompile_required` when the saved plan cannot prove complete boundary coverage. Recompile the original source to produce a current plan. It does not migrate SSMD source.
341
398
  Normal plan loading is TOML-only: `FlowPlan.load()` and plan-inspection commands reject JSON rather than auto-detecting it.
342
399
 
343
400
  ## Planning defaults
344
401
 
345
- The minimal CLI defaults are explicit: `spokenform` is the default text-preparation backend, `tts` is the default pause activation mode, and `spacy off` is the default linguistic-resource policy. With `spacy off`, UtterPlan uses its deterministic fallback tokenizer and analysis and does not depend on an installed spaCy model.
402
+ The CLI and Python planner default to best-effort spaCy enrichment (`auto`). UtterPlan prefers an already-installed compatible model in `sm`, `md`, `lg`, `trf` order; if spaCy or every compatible local model is unavailable, planning continues with deterministic fallback tokens. UtterPlan never downloads models. Use `--spacy off` or `LinguisticsConfig(use_spacy=False)` to opt out, or request an explicit tier to require that local model.
346
403
 
347
404
  Python `PlannerConfig` defaults to `document_format="plain"`; set it to `"ssmd"` when a Python string contains SSMD source.
348
405
 
349
406
  Python `PlannerConfig.renderability_mode` also defaults to safe punctuation-only repair; choose `"strict"` to reject every repair opportunity. Neither mode guesses a symbol's pronunciation or crosses semantic blockers.
350
407
 
351
- `spacy auto` is opt-in. When enabled and a compatible local model is available, UtterPlan may expose richer tokenization, POS tags, lemmas, and tags; `auto` is not the default.
408
+ V7 `token.span`, `pos`, `tag`, `lemma`, and `morph` describe the prepared `segment.text` and are the downstream linguistic context, not engine/model token counts; consumers should not retokenize or rerun spaCy. Optional `replacement.span` / `replacement.source` records compact preparation provenance (for example, `A R T` from `ART`): spans index final prepared segment text, while full source coordinates and maps remain in the optional trace sidecar. Replacement provenance does not change executable hashes or synthesis inputs.
352
409
 
353
410
  ## Python API
354
411
 
@@ -380,6 +437,20 @@ identity. Plain text must be explicitly selected in Python and always requires a
380
437
  language fallback. Existing `UtterancePlanner.plan` remains supported and returns
381
438
  only the plan; TOML is the portable persistence and interchange format.
382
439
 
440
+ The optional RenderPlan producer consumes the stored prepared plan directly:
441
+
442
+ ```python
443
+ from renderplan.codec import load_profile, save_plan
444
+ from utterplan import FlowPlan, build_render_plan
445
+
446
+ source = FlowPlan.load("chapter.utterplan.toml")
447
+ profile = load_profile("voice.renderprofile.toml")
448
+ render_plan = build_render_plan(source, profile)
449
+ save_plan(render_plan, "chapter.renderplan.toml")
450
+ ```
451
+
452
+ Use `build_render_plan(..., previous_plan=previous, preflight=report)` for deterministic refinement; both values are required together. The RenderPlan path rejects historical UtterPlan schemas and does not depend on a renderer or audio runtime. See the [RenderPlan CLI](docs/cli.md#produce-or-replan-a-renderplan) and [Python API](docs/python-api.md#build-and-replan-a-renderplan) guides.
453
+
383
454
  For operational progress from a long-running Python plan, pass `on_progress` to
384
455
  `UtterancePlanner.plan`, `UtterancePlanner.compile`, or `compile_document`. The
385
456
  callback receives typed `PlannerProgressEvent` objects and does not change the plan:
@@ -398,7 +469,16 @@ Callbacks run synchronously and exceptions propagate. See the [Python API guide]
398
469
 
399
470
  ## Renderer-consumer boundary
400
471
 
401
- Renderers consume `FlowPlan.flow` in order. Each `FlowUnit` contains local `FlowSegment` objects with already-prepared speech text, effective language, segment-local token views, semantic pause intents, directives, markers, and optional heading level. Token `start`/`end` offsets are Python-character indexes into that segment's `text`; they are never document-global coordinates. Pause intents express `none`, semantic strengths, or exact authored times and do not prescribe engine-specific duration policy.
472
+ Renderers consume `FlowPlan.flow` in order. Each `FlowUnit` contains local `FlowSegment` objects with already-prepared speech text, effective language, segment-local linguistic token views, canonical boundary opportunities, authored pause intents, directives, markers, and optional heading level. Token `start`/`end` offsets are Python-character indexes into that segment's `text`; they are never document-global coordinates. FlowUnits are fixed paragraph-local blocks, not renderer chunks; a `FlowSegment` is not necessarily one TTS call. `boundary_before` / `boundary_after` hold safe cut opportunities independently from authored `none`, strength, or timed pause intents. Consumers may choose sentence, paragraph, or engine-token-budget chunking and native, automatic, or manual pause realization; linguistic token views are not model-token counts.
473
+
474
+ For renderer-neutral chunk tests and debugging, `preview_plan()` projects stored text and boundaries without changing the plan:
475
+
476
+ ```python
477
+ from utterplan import FlowPlan, preview_plan
478
+
479
+ plan = FlowPlan.load("chapter.utterplan.toml")
480
+ chunks = preview_plan(plan, chunk_by="clause", pause_policy="automatic")
481
+ ```
402
482
 
403
483
  `FlowPlan.document` carries compact document metadata and `FlowPlan.linguistics` identifies the analysis provider without retaining provider documents. Compiler source, preparation maps, diagnostics, and renderability details belong only in an optional trace sidecar. Plans contain no phonemes, model token IDs, models, sessions, renderer configuration, provider documents, or audio. UtterPlan does not fetch media or execute extension handlers.
404
484
  The intended dependency direction is:
@@ -427,9 +507,9 @@ renderer repository rather than UtterPlan's test suite.
427
507
 
428
508
  ## Versions
429
509
 
430
- The package version is dynamically derived from Git tags by setuptools-scm. Package version and UtterPlan schema version are independent. Current `.utterplan.toml` plans use semantic schema v5. Historical JSON Schemas v1–v4 remain immutable and supported through sequential migrations; v5 deliberately has no JSON Schema resource because its canonical executable contract is the validated TOML/FlowPlan model.
510
+ The package version is dynamically derived from Git tags by setuptools-scm. Package version and UtterPlan schema version are independent. Current `.utterplan.toml` plans use semantic schema v7. Historical JSON Schemas v1–v4 remain immutable; schemas v5, v6, and v7 use versioned TOML/FlowPlan contracts and have no JSON Schema resources.
431
511
 
432
- Schema v5 is a breaking redesign around ordered flow units and segment-local semantics. It preserves deterministic output and moves preparation/source provenance to an optional TOML trace sidecar, which does not affect plan identity. Migration converts serialized plan data only: v1–v4 migrate sequentially to v5 without reparsing, NLP, planning, G2P, rendering, or audio processing. The v4-to-v5 projection fails safely when historical token coordinates or pause semantics cannot be verified.
512
+ Schema v5 introduced ordered flow units and segment-local semantics; v6 added optional exact-source provenance. Schema v7 removes renderer-selected root `unit` and adds canonical safe-boundary opportunities separate from authored pause intents; boundaries participate in v7 executable identity. Older schemas retain sequential data-only migration steps through v6, but v6-to-v7 returns `migration.recompile_required` because the complete boundary inventory cannot be reconstructed. Migrations never reparse, run NLP, plan, call G2P, render, or process audio.
433
513
 
434
514
  ## Development
435
515
 
@@ -0,0 +1,279 @@
1
+ [![PyPI - Version](https://img.shields.io/pypi/v/utterplan)](https://pypi.org/project/utterplan/)
2
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/utterplan)
3
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/utterplan)
4
+ [![codecov](https://codecov.io/gh/buchwandler/utterplan/graph/badge.svg?token=cL0qStxvDE)](https://codecov.io/gh/buchwandler/utterplan)
5
+
6
+ # UtterPlan
7
+
8
+ UtterPlan is the canonical, engine-independent semantic compiler from one SSMD
9
+ document (or explicitly selected plain-text document) to an executable speech
10
+ plan. It produces deterministic prepared text, paragraph-local flow units, safe boundary opportunities, authored pause intents, and segment-local
11
+ semantics, portable document metadata, and typed directives for
12
+ independent downstream consumers such as Readio, VoiceRender, and other FlowPlan users. Its book commands read the SSMDBook manifest only for chapter discovery, never modify the manifest or source chapters, and write derived plans to separate directories. It uses TTSReady for written-to-spoken preparation and stops before G2P, synthesis, and audio.
13
+
14
+ ## CLI
15
+
16
+ Compile literal text directly:
17
+
18
+ ```bash
19
+ utterplan compile "Doctor Smith bought 5 kg." --input-format plain --language en-us
20
+ ```
21
+
22
+ Compile a file to a plan file:
23
+
24
+ ```bash
25
+ utterplan compile examples/chapter.ssmd -o chapter.utterplan.toml
26
+ ```
27
+
28
+ SSMD header `language` is authoritative, so `--language` is optional when it is present. Otherwise pass `--language` as the fallback; plain input always requires a language. `--language` never forces a language over SSMD semantics.
29
+
30
+ Use stdin and shell pipelines:
31
+
32
+ ```bash
33
+ cat examples/chapter.ssmd | utterplan compile --lang en-us --input-format ssmd > chapter.utterplan.toml
34
+ ```
35
+
36
+ Compile independent documents incrementally; every successful plan is saved immediately, and ordinary failures do not discard earlier outputs:
37
+
38
+ ```bash
39
+ utterplan compile-many examples/*.ssmd --output-dir build/plans
40
+ ```
41
+
42
+ This writes one `.utterplan.toml` per source plus an atomically refreshed `compile-report.toml`. Terminal diagnostics default to warnings; `--diagnostics all` also prints informational records, while `--diagnostics none` suppresses non-failure details. Failures are always reported, and the operational report retains every diagnostic. Use `--fail-fast` to skip later inputs after a failure, `--force` to replace existing plans, or `--report PATH` to choose another report location.
43
+
44
+ Compile a selected chapter range directly from an editable SSMDBook workspace:
45
+
46
+ ```bash
47
+ cd "Platform Decay - Martha Wells.ssmdbook"
48
+
49
+ utterplan compile-book chapters --chapters 5-17
50
+ ```
51
+
52
+ The command reads the workspace manifest to discover chapters and writes independent v7 plans and a report under `utterplan/`:
53
+
54
+ ```text
55
+ utterplan/
56
+ ├── compile-report.toml
57
+ ├── chapter-0005.utterplan.toml
58
+ ├── chapter-0006.utterplan.toml
59
+ ├── ...
60
+ └── chapter-0017.utterplan.toml
61
+ ```
62
+
63
+ Omit `--chapters` to compile all manifest chapters. Use comma-separated source-number selections for non-contiguous ranges, for example `utterplan compile-book chapters --chapters 1,3-5`. `--diagnostics warnings|all|none` defaults to warnings; failures are always shown and reports retain complete diagnostics. The book root itself is also accepted as input; see the [SSMDBook guide](docs/ssmdbook.md).
64
+
65
+ ## From an SSMDBook to RenderPlans
66
+
67
+ UtterPlan keeps one canonical semantic plan per chapter. A RenderProfile is a
68
+ separate renderer configuration; create and validate it with VoiceRender, then
69
+ project the selected saved plans into a profile-specific RenderPlan collection:
70
+
71
+ ```bash
72
+ voicerender profile create \
73
+ --engine piper \
74
+ --target en_US-amy-medium \
75
+ --language en-US \
76
+ --offline \
77
+ --output narration.renderprofile.toml
78
+ voicerender profile validate narration.renderprofile.toml
79
+
80
+ utterplan compile-book . --chapters 5-17
81
+ utterplan render-book . --chapters 5-17 --profile narration.renderprofile.toml
82
+ ```
83
+
84
+ The book's `utterplan/` directory remains the shared semantic source. The new
85
+ command writes independent renderer-specific plans into a deterministic variant:
86
+
87
+ ```text
88
+ renderplan/
89
+ └── narration--<profile-fingerprint>/
90
+ ├── renderprofile.toml
91
+ ├── renderplan-set.toml
92
+ ├── render-report.toml
93
+ ├── chapter-0005.renderplan.toml
94
+ └── ...
95
+ ```
96
+
97
+ The variant label comes from the profile filename by default; its suffix is the
98
+ first 12 hexadecimal characters of the canonical RenderProfile `profile_id`.
99
+ Changing identity-bearing profile semantics selects a different directory, so
100
+ multiple voices or planning policies can coexist without replacing one another.
101
+ Each variant snapshots the canonical profile; `renderplan-set.toml` records its
102
+ full identity, source profile reference, and chapter plan IDs. `--variant` changes
103
+ the human label without removing the fingerprint, and `--output-root` changes the
104
+ collection root while retaining the automatic variant directory.
105
+
106
+ `render-book` consumes only the existing schema-v7 UtterPlans and shared
107
+ RenderProfile/RenderPlan contract. VoiceRender is not required to run it: VoiceRender
108
+ creates or refreshes engine-backed profiles, then preflights and renders the
109
+ resulting RenderPlans. The single-file `utterplan render-plan` command remains the
110
+ low-level projection and deterministic replan primitive.
111
+
112
+ Related packages: [RenderPlan](https://pypi.org/project/renderplan/) defines the
113
+ shared profile and plan contracts; [VoiceRender](https://pypi.org/project/voicerender/)
114
+ creates engine-backed profiles and performs preflight and synthesis.
115
+
116
+ The CLI also provides:
117
+
118
+ ```bash
119
+ utterplan --version
120
+ utterplan validate chapter.utterplan.toml
121
+ utterplan inspect chapter.utterplan.toml --segment 0
122
+ utterplan inspect chapter.utterplan.toml --boundaries --tokens
123
+ utterplan explain chapter.utterplan.toml --trace chapter.trace.toml
124
+ utterplan inspect-trace chapter.trace.toml --preparation --boundaries
125
+ utterplan inspect-attempt chapter.attempt.toml --issues
126
+ utterplan preview chapter.utterplan.toml --segments 58-62 --chunk-by clause --pause-policy automatic
127
+ utterplan render-plan chapter.utterplan.toml --profile voice.renderprofile.toml -o chapter.renderplan.toml --explain
128
+ ```
129
+
130
+ `explain` presents executable flow as a human-readable speech plan. Use `compile --trace PATH` to retain optional compiler provenance; `explain --trace PATH` and `inspect-trace` read that sidecar separately from the plan.
131
+
132
+ `render-plan` projects only current schema-v7 FlowPlans into the shared RenderPlan contract. Replanning requires both the exact previous RenderPlan and its matching preflight report; UtterPlan refines only failed speech occurrences and does not calculate renderer-specific metrics.
133
+
134
+ `inspect-attempt` reads the separate `utterplan.planning-attempt.v1` TOML artifact,
135
+ not a canonical plan. Use it to review renderability issues and safe repair
136
+ assessments on blocked planning outcomes; such candidates remain inspect-only drafts.
137
+ Canonical plan output is TOML (`.utterplan.toml`) and is written to stdout when no output file is supplied. Status messages use stderr, and existing output files require `--force`. Safe punctuation repair is the default; use `--renderability strict` to reject every repair opportunity.
138
+
139
+ ## SSMD source contract
140
+
141
+ UtterPlan accepts SSMD 0.9 syntax only. Use `document_format="ssmd"` or
142
+ `--input-format ssmd` to compile canonical unversioned fragments as SSMD 0.9.
143
+ Automatic detection recognizes `.ssmd`, `.ssmd.md`, and Markdown files with an SSMD
144
+ version header; ordinary Markdown remains plain text.
145
+
146
+ Migrate older SSMD source before compilation with the SSMD project's migration
147
+ command:
148
+
149
+ ```bash
150
+ ssmd migrate old.ssmd --to 0.9
151
+ ```
152
+
153
+ `utterplan migrate` handles only serialized plan data; v6-to-v7 migration refuses with `migration.recompile_required` when the saved plan cannot prove complete boundary coverage. Recompile the original source to produce a current plan. It does not migrate SSMD source.
154
+ Normal plan loading is TOML-only: `FlowPlan.load()` and plan-inspection commands reject JSON rather than auto-detecting it.
155
+
156
+ ## Planning defaults
157
+
158
+ The CLI and Python planner default to best-effort spaCy enrichment (`auto`). UtterPlan prefers an already-installed compatible model in `sm`, `md`, `lg`, `trf` order; if spaCy or every compatible local model is unavailable, planning continues with deterministic fallback tokens. UtterPlan never downloads models. Use `--spacy off` or `LinguisticsConfig(use_spacy=False)` to opt out, or request an explicit tier to require that local model.
159
+
160
+ Python `PlannerConfig` defaults to `document_format="plain"`; set it to `"ssmd"` when a Python string contains SSMD source.
161
+
162
+ Python `PlannerConfig.renderability_mode` also defaults to safe punctuation-only repair; choose `"strict"` to reject every repair opportunity. Neither mode guesses a symbol's pronunciation or crosses semantic blockers.
163
+
164
+ V7 `token.span`, `pos`, `tag`, `lemma`, and `morph` describe the prepared `segment.text` and are the downstream linguistic context, not engine/model token counts; consumers should not retokenize or rerun spaCy. Optional `replacement.span` / `replacement.source` records compact preparation provenance (for example, `A R T` from `ART`): spans index final prepared segment text, while full source coordinates and maps remain in the optional trace sidecar. Replacement provenance does not change executable hashes or synthesis inputs.
165
+
166
+ ## Python API
167
+
168
+ ```python
169
+ from utterplan import PlannerConfig, FlowPlan, compile_document
170
+
171
+ ssmd_source = """---
172
+ ssmd_version: "0.9"
173
+ language: en-GB
174
+ ---
175
+ Hello [world]{emphasis="strong"}.
176
+ """
177
+ result = compile_document(
178
+ ssmd_source,
179
+ input_format="ssmd",
180
+ config=PlannerConfig(language="en-us"), # fallback; header language wins
181
+ trace=True,
182
+ )
183
+ plan = result.plan
184
+ assert result.trace is not None
185
+ plan.save("example.utterplan.toml")
186
+ assert FlowPlan.load("example.utterplan.toml") == plan
187
+ ```
188
+
189
+ `compile_document` is the stable public one-document API shared by consumers. The
190
+ immutable `CompileResult` contains the semantic plan, diagnostics, and optional
191
+ explanatory trace. Trace is not serialized in the plan and does not affect its
192
+ identity. Plain text must be explicitly selected in Python and always requires a
193
+ language fallback. Existing `UtterancePlanner.plan` remains supported and returns
194
+ only the plan; TOML is the portable persistence and interchange format.
195
+
196
+ The optional RenderPlan producer consumes the stored prepared plan directly:
197
+
198
+ ```python
199
+ from renderplan.codec import load_profile, save_plan
200
+ from utterplan import FlowPlan, build_render_plan
201
+
202
+ source = FlowPlan.load("chapter.utterplan.toml")
203
+ profile = load_profile("voice.renderprofile.toml")
204
+ render_plan = build_render_plan(source, profile)
205
+ save_plan(render_plan, "chapter.renderplan.toml")
206
+ ```
207
+
208
+ Use `build_render_plan(..., previous_plan=previous, preflight=report)` for deterministic refinement; both values are required together. The RenderPlan path rejects historical UtterPlan schemas and does not depend on a renderer or audio runtime. See the [RenderPlan CLI](docs/cli.md#produce-or-replan-a-renderplan) and [Python API](docs/python-api.md#build-and-replan-a-renderplan) guides.
209
+
210
+ For operational progress from a long-running Python plan, pass `on_progress` to
211
+ `UtterancePlanner.plan`, `UtterancePlanner.compile`, or `compile_document`. The
212
+ callback receives typed `PlannerProgressEvent` objects and does not change the plan:
213
+
214
+ ```python
215
+ from utterplan import PlannerConfig, UtterancePlanner
216
+
217
+ events = []
218
+ plan = UtterancePlanner(PlannerConfig(language="en-us")).plan(
219
+ text,
220
+ on_progress=events.append,
221
+ )
222
+ ```
223
+
224
+ Callbacks run synchronously and exceptions propagate. See the [Python API guide](docs/python-api.md#planner-progress-callbacks) for event fields, model-load events, and reuse behavior.
225
+
226
+ ## Renderer-consumer boundary
227
+
228
+ Renderers consume `FlowPlan.flow` in order. Each `FlowUnit` contains local `FlowSegment` objects with already-prepared speech text, effective language, segment-local linguistic token views, canonical boundary opportunities, authored pause intents, directives, markers, and optional heading level. Token `start`/`end` offsets are Python-character indexes into that segment's `text`; they are never document-global coordinates. FlowUnits are fixed paragraph-local blocks, not renderer chunks; a `FlowSegment` is not necessarily one TTS call. `boundary_before` / `boundary_after` hold safe cut opportunities independently from authored `none`, strength, or timed pause intents. Consumers may choose sentence, paragraph, or engine-token-budget chunking and native, automatic, or manual pause realization; linguistic token views are not model-token counts.
229
+
230
+ For renderer-neutral chunk tests and debugging, `preview_plan()` projects stored text and boundaries without changing the plan:
231
+
232
+ ```python
233
+ from utterplan import FlowPlan, preview_plan
234
+
235
+ plan = FlowPlan.load("chapter.utterplan.toml")
236
+ chunks = preview_plan(plan, chunk_by="clause", pause_policy="automatic")
237
+ ```
238
+
239
+ `FlowPlan.document` carries compact document metadata and `FlowPlan.linguistics` identifies the analysis provider without retaining provider documents. Compiler source, preparation maps, diagnostics, and renderability details belong only in an optional trace sidecar. Plans contain no phonemes, model token IDs, models, sessions, renderer configuration, provider documents, or audio. UtterPlan does not fetch media or execute extension handlers.
240
+ The intended dependency direction is:
241
+
242
+ ```text
243
+ PyKokoro or another renderer -> UtterPlan
244
+ ```
245
+
246
+ UtterPlan does not depend on PyKokoro, G2P engines, ONNX Runtime, or audio
247
+ packages. Consumer-specific compatibility tests belong in the consuming
248
+ renderer repository rather than UtterPlan's test suite.
249
+
250
+ ## Documentation
251
+
252
+ - [Getting started](docs/getting-started.md)
253
+ - [CLI](docs/cli.md)
254
+ - [SSMDBook workflows](docs/ssmdbook.md)
255
+ - [Format and schema](docs/format.md)
256
+ - [Consumer guide](docs/consumer-guide.md)
257
+ - [Architecture](docs/architecture.md)
258
+ - [Coordinates](docs/coordinate-spaces.md)
259
+ - [PyKokoro integration](docs/pykokoro-integration.md)
260
+ - [Python API](docs/python-api.md)
261
+ - [Debugging](docs/debugging.md)
262
+ - [Changelog](docs/changelog.md)
263
+
264
+ ## Versions
265
+
266
+ The package version is dynamically derived from Git tags by setuptools-scm. Package version and UtterPlan schema version are independent. Current `.utterplan.toml` plans use semantic schema v7. Historical JSON Schemas v1–v4 remain immutable; schemas v5, v6, and v7 use versioned TOML/FlowPlan contracts and have no JSON Schema resources.
267
+
268
+ Schema v5 introduced ordered flow units and segment-local semantics; v6 added optional exact-source provenance. Schema v7 removes renderer-selected root `unit` and adds canonical safe-boundary opportunities separate from authored pause intents; boundaries participate in v7 executable identity. Older schemas retain sequential data-only migration steps through v6, but v6-to-v7 returns `migration.recompile_required` because the complete boundary inventory cannot be reconstructed. Migrations never reparse, run NLP, plan, call G2P, render, or process audio.
269
+
270
+ ## Development
271
+
272
+ ```bash
273
+ python -m pip install -e '.[dev,docs]'
274
+ python -m pytest -q
275
+ ruff check .
276
+ mypy utterplan
277
+ python -m build
278
+ sphinx-build -W --keep-going -b html docs docs/_build/html
279
+ ```