utterplan 0.6.3__tar.gz → 0.6.4__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.
- utterplan-0.6.4/MANIFEST.in +2 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/PKG-INFO +11 -1
- {utterplan-0.6.3 → utterplan-0.6.4}/README.md +10 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/architecture.md +32 -1
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/changelog.md +9 -1
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/cli.md +15 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/index.md +2 -0
- utterplan-0.6.4/docs/ssmdoc.md +64 -0
- utterplan-0.6.4/tests/fixtures/ssmdoc-v1/story.ssmdoc/document.ssmd.md +6 -0
- utterplan-0.6.4/tests/fixtures/ssmdoc-v1/story.ssmdoc/manifest.json +21 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_cli.py +52 -0
- utterplan-0.6.4/tests/test_cli_ssmdoc.py +279 -0
- utterplan-0.6.4/tests/test_document_workspace.py +248 -0
- utterplan-0.6.4/tests/test_source_input.py +104 -0
- utterplan-0.6.4/tests/test_ssmdoc.py +250 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/__init__.py +14 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/_version.py +3 -3
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/cli.py +133 -41
- utterplan-0.6.4/utterplan/document_workspace.py +194 -0
- utterplan-0.6.4/utterplan/source_input.py +121 -0
- utterplan-0.6.4/utterplan/ssmdoc.py +191 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/PKG-INFO +11 -1
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/SOURCES.txt +10 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/scm_file_list.json +19 -0
- utterplan-0.6.4/utterplan.egg-info/scm_version.json +8 -0
- utterplan-0.6.3/MANIFEST.in +0 -1
- utterplan-0.6.3/utterplan.egg-info/scm_version.json +0 -8
- {utterplan-0.6.3 → utterplan-0.6.4}/.codecrate.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/.github/workflows/codecov.yml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/.github/workflows/pre-commit.yml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/.github/workflows/python-publish.yml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/.github/workflows/tests.yml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/.gitignore +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/.pre-commit-config.yaml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/AGENTS.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/LICENSE +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/NOTICE +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/Makefile +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/conf.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/consumer-guide.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/coordinate-spaces.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/debugging.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/format.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/getting-started.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/make.bat +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/phase-a-completion.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/pykokoro-integration.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/python-api.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/requirements.txt +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/docs/ssmdbook.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/examples/basic.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/examples/chapter.ssmd +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/examples/compile_file.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/examples/inspect_plan.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/examples/multilingual.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/examples/ssmd_example.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/pyproject.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/setup.cfg +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/spec/schemas/v1.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/spec/schemas/v2.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/spec/schemas/v3.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/spec/schemas/v4.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/spec/utterplan.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/compiler_helpers.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/fixtures/canonical_consumer_contract.expected.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/fixtures/canonical_consumer_contract.ssmd +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/fixtures/ssmd_09_comprehensive.ssmd +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/basic_en.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/directives.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/markers.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/multilingual.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/parenthetical.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/spokenform_offsets.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/ssmd_09_comprehensive.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/golden/ssmd_breaks.utterplan.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/cases.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/basic_en.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/directives.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/markers.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/multilingual.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/parenthetical.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/spokenform_offsets.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/ssmd_09_comprehensive.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/fixtures/v4/ssmd_breaks.utterplan.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/goldens/multilingual_preparation.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/goldens/spokenform_numbers.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/goldens/ssmd_break.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/goldens/ssmd_language_detection.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/goldens/ssmd_marker.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/goldens/ssmd_pronunciation.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/test_legacy_json_import.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/migration/test_semantic_migration_regressions.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/README.md +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/basic.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/directives.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/markers.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/multilingual.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/parenthetical.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/spokenform_offsets.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v1/ssmd_breaks.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v2/directives.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v3/multilingual.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v3/ssmd_09_comprehensive.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/schema_history/v5/canonical_consumer_contract.toml +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_atomic_io.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_batch.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_book_api.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_book_replan.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_boundaries.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_cli_batch.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_cli_render_book.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_cli_replan_book.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_cli_ssmdbook.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_compiler.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_consumer_contract.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_consumer_readiness.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_coordinates.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_dependency_architecture.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_directives.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_explain.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_fake_renderer.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_flow_compilation.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_format.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_golden.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_identity_units.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_import_boundaries.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_language.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_linguistics.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_migration_framework.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_migration_v3_to_v4.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_migration_v4_to_v5.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_pauses.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_planner.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_preparation.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_preview.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_progress.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_public_identity.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderability.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderability_repair.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderplan_batch.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderplan_dependency_contract.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderplan_export.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderplan_replan.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_renderplan_v7_contract.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_replacement_provenance.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_schema_history.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_schema_registry.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_schema_strict.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_schema_v6_source.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_schema_v7.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_segment_topology.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_segmentation_adapter.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_segmentation_recovery.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_semantic_boundaries.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_sentence_topology.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_ssmd_09_parser.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_ssmd_substitution_preparation.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_ssmdbook.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_token_topology.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_toml_format.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_ttsready_preparation_adapter.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/tests/test_version.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/__main__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/atomic_io.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/attempt_serialization.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/attempts.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/batch.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/book.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/book_replan.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/boundaries.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/codecs/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/codecs/v4_toml.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/codecs/v5_toml.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/codecs/v6_toml.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/codecs/v7_toml.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/compiler.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/config.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/diagnostics.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/directives.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/exceptions.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/explain.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/flow_projection.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/format.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/hashing.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/language.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/linguistics.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migration.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/registry.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/v1_to_v2.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/v2_to_v3.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/v3_to_v4.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/v4_to_v5.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/v5_to_v6.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/migrations/v6_to_v7.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/model.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/parsers.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/pauses.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/planner.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/preparation.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/preview.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/progress.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/py.typed +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/renderability.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/renderplan_batch.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/renderplan_export.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/renderplan_grouping.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/renderplan_projection.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/renderplan_replan.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/schema_registry.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/schemas/v1.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/schemas/v2.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/schemas/v3.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/schemas/v4.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/segmentation.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/serialization.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/spacy_models.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/spans.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/ssmdbook.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/document/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/document/plain.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/document/ssmd.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/preparation/__init__.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/preparation/identity.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/preparation/ssmd.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/preparation/ttsready.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/stages/protocols.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/token_topology.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/toml_codec.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/trace_codec.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/units.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/utterplan.schema.json +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan/versioning.py +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/dependency_links.txt +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/entry_points.txt +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/requires.txt +0 -0
- {utterplan-0.6.3 → utterplan-0.6.4}/utterplan.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: utterplan
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
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
|
|
@@ -285,6 +285,16 @@ utterplan compile-many examples/*.ssmd --output-dir build/plans
|
|
|
285
285
|
|
|
286
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.
|
|
287
287
|
|
|
288
|
+
## Compile an SSMDDoc workspace
|
|
289
|
+
|
|
290
|
+
Compile one editable `.ssmdoc` directory as one SSMD document and one FlowPlan:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
utterplan compile-document story.ssmdoc
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
By default this writes `story.ssmdoc/.readio/utterplan/document-0001.utterplan.toml` and a `compile-report.toml`. The current `document.ssmd.md` is authoritative even if its manifest digest is stale; UtterPlan leaves the source and manifest untouched. This is separate from the multi-chapter SSMDBook workflow below. See the [SSMDDoc workspace guide](docs/ssmdoc.md) for manifest safety, output options, and the Python API.
|
|
297
|
+
|
|
288
298
|
Compile a selected chapter range directly from an editable SSMDBook workspace:
|
|
289
299
|
|
|
290
300
|
```bash
|
|
@@ -41,6 +41,16 @@ utterplan compile-many examples/*.ssmd --output-dir build/plans
|
|
|
41
41
|
|
|
42
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
43
|
|
|
44
|
+
## Compile an SSMDDoc workspace
|
|
45
|
+
|
|
46
|
+
Compile one editable `.ssmdoc` directory as one SSMD document and one FlowPlan:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
utterplan compile-document story.ssmdoc
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
By default this writes `story.ssmdoc/.readio/utterplan/document-0001.utterplan.toml` and a `compile-report.toml`. The current `document.ssmd.md` is authoritative even if its manifest digest is stale; UtterPlan leaves the source and manifest untouched. This is separate from the multi-chapter SSMDBook workflow below. See the [SSMDDoc workspace guide](docs/ssmdoc.md) for manifest safety, output options, and the Python API.
|
|
53
|
+
|
|
44
54
|
Compile a selected chapter range directly from an editable SSMDBook workspace:
|
|
45
55
|
|
|
46
56
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Architecture Documentation"
|
|
3
|
-
version:
|
|
3
|
+
version: 2
|
|
4
4
|
generator: "archledger 0.4.0"
|
|
5
5
|
arc42_template_version: "9.0-EN"
|
|
6
6
|
---
|
|
@@ -60,6 +60,10 @@ The compiler still handles one document at a time; the CLI's explicit SSMDBook
|
|
|
60
60
|
commands (`compile-book`, `render-book`) discover chapters and orchestrate per-file
|
|
61
61
|
workspaces without changing book content.
|
|
62
62
|
|
|
63
|
+
## Document workspaces
|
|
64
|
+
|
|
65
|
+
An editable `ssmdconvert.document` v1 workspace (`.ssmdoc`) is another source path for the one-document compiler. `compile-document` discovers its canonical current SSMD document and writes one FlowPlan plus an operational report under `.readio/utterplan/`; it does not create a book or alter workspace source material.
|
|
66
|
+
|
|
63
67
|
## Requirements Overview
|
|
64
68
|
|
|
65
69
|
<!-- archledger: no accepted records for this section yet -->
|
|
@@ -164,6 +168,12 @@ UtterPlan depends directly on SSMD, PhraseSplit, and TTSReady:
|
|
|
164
168
|
- Renderer chunking, token-budget, and pause-realization choices.
|
|
165
169
|
- Consumer-specific compatibility testing.
|
|
166
170
|
|
|
171
|
+
## SSMDDoc source workspace
|
|
172
|
+
|
|
173
|
+
- An editable `.ssmdoc` directory is discovered by its `ssmdconvert.document` v1 manifest, not by its directory suffix. Its canonical `document.ssmd.md` is the current compiler input.
|
|
174
|
+
- The workspace remains one document to one FlowPlan. Manifest digests are provenance only for editable directories: UtterPlan compiles current content and never refreshes the manifest or edits the source.
|
|
175
|
+
- `compile-document` is single-document convenience, not book orchestration. ZIP bundles, fake chapters, and an `ssmdconvert` runtime dependency are outside the contract.
|
|
176
|
+
|
|
167
177
|
## Business Context
|
|
168
178
|
|
|
169
179
|
<!-- archledger: no accepted records for this section yet -->
|
|
@@ -253,6 +263,10 @@ provenance, diagnostics, and renderability evidence in a separate versioned TOML
|
|
|
253
263
|
sidecar. Trace state is not renderer input and does not affect plan identity or
|
|
254
264
|
TOML.
|
|
255
265
|
|
|
266
|
+
## Document workspace orchestration
|
|
267
|
+
|
|
268
|
+
`ssmdoc.py` performs read-only manifest discovery and canonical path safety checks; `source_input.py` centralizes standalone-file and `.ssmdoc` source loading; and `document_workspace.py` delegates one stable `document-0001` CompileRequest to the existing batch persistence/report path. The CLI and public package API expose `compile-document` and `compile_ssmdoc()` without shadowing the in-memory `compile_document()` compiler.
|
|
269
|
+
|
|
256
270
|
<!-- archledger: no accepted records for this section yet -->
|
|
257
271
|
|
|
258
272
|
# Runtime View
|
|
@@ -327,6 +341,17 @@ accepts TOML only and rejects JSON.
|
|
|
327
341
|
RenderPlan and its matching preflight report are required together; UtterPlan does
|
|
328
342
|
not calculate renderer-specific metrics.
|
|
329
343
|
|
|
344
|
+
## Scenario: compile an editable SSMDDoc workspace
|
|
345
|
+
|
|
346
|
+
```text
|
|
347
|
+
ssmdconvert.document v1 manifest + current document.ssmd.md
|
|
348
|
+
-> utterplan compile-document
|
|
349
|
+
-> one FlowPlan at .readio/utterplan/document-0001.utterplan.toml
|
|
350
|
+
+ one compile-report.toml
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The manifest validates the workspace contract and safe canonical path; SSMD 0.9 content is compiled by the existing single-document path. A stale stored digest does not block editable-directory compilation, and UtterPlan does not modify source material or model the document as a chapter.
|
|
354
|
+
|
|
330
355
|
<!-- archledger: no accepted records for this section yet -->
|
|
331
356
|
|
|
332
357
|
# Deployment View
|
|
@@ -355,6 +380,8 @@ or audio dependencies:
|
|
|
355
380
|
- **Quality gate.** `python -m pytest -q`, `ruff check .`, and `mypy utterplan` run
|
|
356
381
|
against the self-contained repository test suite.
|
|
357
382
|
|
|
383
|
+
- **Editable document workspaces.** `.ssmdoc` v1 is consumed as a stable file-format contract with the standard library; `ssmdconvert` is not a runtime dependency. `compile-document` writes derived plans under the reserved `.readio/utterplan/` directory without changing source files.
|
|
384
|
+
|
|
358
385
|
<!-- archledger: no accepted records for this section yet -->
|
|
359
386
|
|
|
360
387
|
# Cross-cutting Concepts
|
|
@@ -484,6 +511,8 @@ drafts and never become canonical plans.
|
|
|
484
511
|
compiled plan; replanning is a separate operation. _Rationale:_ schema evolution
|
|
485
512
|
must not force package-version coupling or silent replans.
|
|
486
513
|
|
|
514
|
+
- **AD: SSMDDoc is one document, not a book.** An `ssmdconvert.document` v1 workspace resolves to its current canonical SSMD source and the existing one-document compiler, producing one FlowPlan. Editable directory digests do not override current source content; UtterPlan neither refreshes nor mutates the workspace. _Rationale:_ preserve one-document/one-plan semantics and keep source-workspace ownership in `ssmdconvert`.
|
|
515
|
+
|
|
487
516
|
<!-- archledger: no accepted records for this section yet -->
|
|
488
517
|
|
|
489
518
|
# Quality Requirements
|
|
@@ -584,4 +613,6 @@ drafts and never become canonical plans.
|
|
|
584
613
|
| Planning attempt | Inspect-only `utterplan.planning-attempt.v1` artifact for reviewing blocked renderability outcomes; never a canonical plan. |
|
|
585
614
|
| plan_id | Deterministic renderer-independent plan identity; v7 `utterplan-flow-v3` includes boundaries and never hashes TOML formatting. |
|
|
586
615
|
|
|
616
|
+
| SSMDDoc | Editable single-document workspace using the `ssmdconvert.document` v1 manifest; `compile-document` compiles the current canonical SSMD file to one FlowPlan without mutating source. |
|
|
617
|
+
|
|
587
618
|
<!-- archledger: no accepted records for this section yet -->
|
|
@@ -4,7 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
## [v0.6.
|
|
7
|
+
## [v0.6.4] - 2026-10-19
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added compile-document for editable SSMDDoc workspaces with safe source loading and plan reports
|
|
12
|
+
|
|
13
|
+
<!-- generated by releaseledger -->
|
|
14
|
+
|
|
15
|
+
## [v0.6.3] - 2026-10-15
|
|
8
16
|
|
|
9
17
|
### Added
|
|
10
18
|
|
|
@@ -124,6 +124,21 @@ Compile reports use format version 2. Each successful item records its resolved
|
|
|
124
124
|
|
|
125
125
|
Shared planning options include `--language`, `--input-format auto|plain|ssmd`, `--text-preparation`, `--spacy`, and `--renderability`. `--diagnostics warnings|all|none` controls terminal detail (default `warnings`): warnings mode suppresses informational diagnostics, all includes them, and none suppresses non-failure diagnostics. Failures are always reported, and the operational report always retains complete diagnostics. Progress, selected diagnostic details, actionable errors, and final counts are written to stderr. Exit status is 0 if every input succeeds, 1 if any input fails, and 2 for usage errors such as output collisions. `--fail-fast` skips later requests after the first failure but preserves all completed outputs and updates the report.
|
|
126
126
|
|
|
127
|
+
## Compile one SSMDDoc workspace
|
|
128
|
+
|
|
129
|
+
`compile-document` reads a single editable `ssmdconvert.document` v1 workspace and compiles its canonical SSMD 0.9 document into one plan. It accepts the workspace root or its canonical `document.ssmd.md` path:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
utterplan compile-document INPUT [--output-dir PATH] [--language LANGUAGE]
|
|
133
|
+
[--text-preparation {auto,ssmd,spokenform,identity}]
|
|
134
|
+
[--renderability {strict,repair}] [--spacy {auto,off,sm,md,lg,trf}]
|
|
135
|
+
[--force] [--report PATH] [--diagnostics {warnings,all,none}]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The default output directory is `<workspace>/.readio/utterplan/`, containing `document-0001.utterplan.toml` and `compile-report.toml`. `--output-dir` and `--report` override those locations. Existing plan files require `--force`; output collisions with the source or manifest are rejected. A document header's language is authoritative; `--language`/`--lang` is a fallback when it is absent.
|
|
139
|
+
|
|
140
|
+
Editable content wins over the stored manifest digest: compilation reads the current `document.ssmd.md` but never modifies the manifest or source. The command has no `--input-format`, chapter selection, or `--fail-fast`; it handles one document. The existing `compile` and `validate` commands also accept the workspace root or canonical document path, while `compile` retains its normal stdout/`-o` behavior. This is not `compile-book`: an SSMDDoc creates one plan, while an SSMDBook orchestrates independent chapter plans. ZIP bundles are not supported and `ssmdconvert` is not a runtime dependency. See the [SSMDDoc guide](ssmdoc.md) for format validation and the `compile_ssmdoc()` Python API.
|
|
141
|
+
|
|
127
142
|
## Compile chapters from an SSMDBook workspace
|
|
128
143
|
|
|
129
144
|
`compile-book` discovers selected SSMD chapters from an editable `.ssmdbook` manifest and compiles each chapter independently through the batch compiler. Pass either the book directory or its canonical `chapters/` directory:
|
|
@@ -9,6 +9,7 @@ speech plans. It stops before G2P and synthesis.
|
|
|
9
9
|
|
|
10
10
|
getting-started
|
|
11
11
|
cli
|
|
12
|
+
SSMDDoc workspace support <ssmdoc>
|
|
12
13
|
SSMDBook and RenderPlan workflow <ssmdbook>
|
|
13
14
|
consumer-guide
|
|
14
15
|
format
|
|
@@ -27,4 +28,5 @@ changelog
|
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
The [SSMDBook guide](ssmdbook.md) covers workspace compilation and the profile-specific `render-book` workflow, including how RenderPlan and VoiceRender fit after semantic planning.
|
|
31
|
+
The [SSMDDoc guide](ssmdoc.md) covers one-document workspace compilation, source immutability, and how it differs from an SSMDBook.
|
|
30
32
|
The package version and the UtterPlan schema version are independent. Schema v7 is the only version accepted by normal loading and execution. Schema-v5/v6 TOML and schemas v1–v4 remain immutable historical migration inputs; the sequential path reaches v6, while v6-to-v7 requires recompiling the original source.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# SSMDDoc workspace support
|
|
2
|
+
|
|
3
|
+
UtterPlan can compile an editable `.ssmdoc` workspace as one SSMD 0.9 document. This is a workspace input convenience around the existing one-document compiler; it is not a book format and does not create chapter plans.
|
|
4
|
+
|
|
5
|
+
## Workspace contract
|
|
6
|
+
|
|
7
|
+
A workspace is identified by a `manifest.json` whose `format` is `ssmdconvert.document` and whose `schema_version` is `1`. UtterPlan supports the v1 contract and expects the canonical document entry to identify `document-0001` at the normalized relative path `document.ssmd.md`. The source is SSMD 0.9.
|
|
8
|
+
|
|
9
|
+
The current `document.ssmd.md` is authoritative. Its manifest digest and character count are metadata, not a lock: if you edit the document and its recorded digest becomes stale, UtterPlan compiles the current file. It never rewrites the manifest, refreshes hashes, or edits source content. The `.readio/utterplan/` directory is reserved for derived UtterPlan artifacts.
|
|
10
|
+
|
|
11
|
+
Discovery and path handling are deliberately strict. The manifest must be valid UTF-8 JSON within the 2 MiB limit, and the document is limited to 64 MiB. UtterPlan checks the canonical path and workspace containment, rejects unsupported manifest versions and unsafe paths, and does not follow a document path outside the workspace. It reads no source-media files. `ssmdconvert` is not a runtime dependency: UtterPlan consumes the published file-format contract directly.
|
|
12
|
+
|
|
13
|
+
ZIP bundles are not supported by this workflow; use an editable directory workspace.
|
|
14
|
+
|
|
15
|
+
## Compile a workspace
|
|
16
|
+
|
|
17
|
+
Run `compile-document` with the workspace directory (the suffix need not be `.ssmdoc` if the manifest identifies it):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
utterplan compile-document story.ssmdoc
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It writes one plan and one operational report by default:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
story.ssmdoc/.readio/utterplan/
|
|
27
|
+
├── document-0001.utterplan.toml
|
|
28
|
+
└── compile-report.toml
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The report describes the single compilation and is not a FlowPlan. You can override the destination and report path:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
utterplan compile-document story.ssmdoc \
|
|
35
|
+
--output-dir build/story-plan \
|
|
36
|
+
--report build/story-report.toml \
|
|
37
|
+
--language en-US \
|
|
38
|
+
--spacy off
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Options are `--output-dir`, `--report`, `--language`/`--lang`, `--text-preparation`, `--renderability`, `--spacy`, `--diagnostics`, and `--force`. SSMD header language remains authoritative; pass `--language` as a fallback when the document has no language. Existing plan outputs are protected unless `--force` is supplied. The report may not collide with the plan, manifest, or source document. `compile-document` is a single-workspace command, so it has no chapter selector or `--fail-fast` option.
|
|
42
|
+
|
|
43
|
+
The existing `compile` and `validate` commands also accept the workspace root or its canonical `document.ssmd.md` file as input. `compile` keeps its normal stdout/`-o` behavior; use `compile-document` when you want the workspace-oriented default output directory and report. `validate` compiles the source for validation without writing a plan.
|
|
44
|
+
|
|
45
|
+
## Python API
|
|
46
|
+
|
|
47
|
+
Use `compile_ssmdoc()` for file-backed workspace compilation. The existing `compile_document()` remains the in-memory text API:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from utterplan import compile_ssmdoc
|
|
51
|
+
|
|
52
|
+
result = compile_ssmdoc("story.ssmdoc", language="en-US")
|
|
53
|
+
if result.failed:
|
|
54
|
+
raise RuntimeError(f"workspace compilation failed; see {result.report_path}")
|
|
55
|
+
print(result.plan_path)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The result provides the workspace and source paths, output and report paths, and the one-item batch outcome. Options include `output_dir`, `report`, `language`, `planner_config`, `force`, `diagnostics`, and progress/start callbacks. The default artifacts match the CLI: `.readio/utterplan/document-0001.utterplan.toml` and `compile-report.toml`.
|
|
59
|
+
|
|
60
|
+
## SSMDDoc is not SSMDBook
|
|
61
|
+
|
|
62
|
+
An SSMDDoc contains one editable document and produces one canonical FlowPlan. An SSMDBook manifest discovers multiple independent chapter documents; `compile-book` writes one plan per selected chapter and never builds a book-level combined plan. Use [SSMDBook workflows](ssmdbook.md) for book compilation and RenderPlan projection.
|
|
63
|
+
|
|
64
|
+
Neither workspace workflow changes its source manifest or source documents. UtterPlan stops at semantic FlowPlans; engine selection, G2P, synthesis, and audio remain downstream.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": "ssmdconvert.document",
|
|
3
|
+
"schema_version": 1,
|
|
4
|
+
"ssmd_version": "0.9",
|
|
5
|
+
"metadata": {
|
|
6
|
+
"title": "UtterPlan document fixture",
|
|
7
|
+
"language": "en-US"
|
|
8
|
+
},
|
|
9
|
+
"source": {
|
|
10
|
+
"format": "markdown",
|
|
11
|
+
"media_type": "text/markdown",
|
|
12
|
+
"name": "story.md",
|
|
13
|
+
"sha256": "0000000000000000000000000000000000000000000000000000000000000000"
|
|
14
|
+
},
|
|
15
|
+
"document": {
|
|
16
|
+
"id": "document-0001",
|
|
17
|
+
"path": "document.ssmd.md",
|
|
18
|
+
"char_count": 87,
|
|
19
|
+
"sha256": "ed67851d91bb3d82c08020d1cdfef99102632a62dbeea46ee1793cf8fa9a49c3"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
4
|
import json
|
|
5
|
+
import shutil
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
import pytest
|
|
@@ -147,6 +148,57 @@ def test_compile_auto_detects_ssmd_suffix(
|
|
|
147
148
|
assert _payload(capsys)["document"]["format"] == "ssmd"
|
|
148
149
|
|
|
149
150
|
|
|
151
|
+
def test_compile_ssmdoc_directory_matches_canonical_file(
|
|
152
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
153
|
+
) -> None:
|
|
154
|
+
fixture = Path(__file__).parent / "fixtures/ssmdoc-v1/story.ssmdoc"
|
|
155
|
+
workspace = tmp_path / "story.ssmdoc"
|
|
156
|
+
shutil.copytree(fixture, workspace)
|
|
157
|
+
source = workspace / "document.ssmd.md"
|
|
158
|
+
|
|
159
|
+
assert main(["compile", str(source), "--spacy", "off"]) == 0
|
|
160
|
+
direct = FlowPlan.from_toml(capsys.readouterr().out)
|
|
161
|
+
assert main(["compile", str(workspace), "--spacy", "off"]) == 0
|
|
162
|
+
from_workspace = FlowPlan.from_toml(capsys.readouterr().out)
|
|
163
|
+
|
|
164
|
+
assert from_workspace.plan_id == direct.plan_id
|
|
165
|
+
assert from_workspace.to_dict() == direct.to_dict()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_validate_accepts_dirty_ssmdoc_workspace(
|
|
169
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
170
|
+
) -> None:
|
|
171
|
+
fixture = Path(__file__).parent / "fixtures/ssmdoc-v1/story.ssmdoc"
|
|
172
|
+
workspace = tmp_path / "dirty.ssmdoc"
|
|
173
|
+
shutil.copytree(fixture, workspace)
|
|
174
|
+
manifest = workspace / "manifest.json"
|
|
175
|
+
manifest_before = manifest.read_bytes()
|
|
176
|
+
source = workspace / "document.ssmd.md"
|
|
177
|
+
source.write_text(
|
|
178
|
+
'---\nssmd_version: "0.9"\nlanguage: en-US\n---\n\nEdited current document.\n',
|
|
179
|
+
encoding="utf-8",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
assert main(["validate", str(workspace)]) == 0
|
|
183
|
+
captured = capsys.readouterr()
|
|
184
|
+
assert "valid" in captured.out
|
|
185
|
+
assert "input format: ssmd" in captured.out
|
|
186
|
+
assert manifest.read_bytes() == manifest_before
|
|
187
|
+
assert "Edited current document." in source.read_text(encoding="utf-8")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_compile_and_validate_reject_arbitrary_directories(
|
|
191
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
192
|
+
) -> None:
|
|
193
|
+
directory = tmp_path / "not-a-source"
|
|
194
|
+
directory.mkdir()
|
|
195
|
+
|
|
196
|
+
assert main(["compile", str(directory)]) == 1
|
|
197
|
+
assert "unsupported source directory" in capsys.readouterr().err
|
|
198
|
+
assert main(["validate", str(directory)]) == 1
|
|
199
|
+
assert "unsupported source directory" in capsys.readouterr().err
|
|
200
|
+
|
|
201
|
+
|
|
150
202
|
def test_compile_explicit_ssmd_from_stdin(
|
|
151
203
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
152
204
|
) -> None:
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
import tomlkit
|
|
9
|
+
|
|
10
|
+
from utterplan.cli import build_parser, main
|
|
11
|
+
from utterplan.model import FlowPlan
|
|
12
|
+
|
|
13
|
+
FIXTURE = Path(__file__).parent / "fixtures/ssmdoc-v1/story.ssmdoc"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _workspace(
|
|
17
|
+
tmp_path: Path, name: str = "story.ssmdoc", *, text: str = "Original content."
|
|
18
|
+
) -> Path:
|
|
19
|
+
root = tmp_path / name
|
|
20
|
+
shutil.copytree(FIXTURE, root)
|
|
21
|
+
(root / "document.ssmd.md").write_text(
|
|
22
|
+
f'---\nssmd_version: "0.9"\nlanguage: en-US\n---\n\n{text}\n',
|
|
23
|
+
encoding="utf-8",
|
|
24
|
+
)
|
|
25
|
+
return root
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _report(path: Path) -> dict[str, object]:
|
|
29
|
+
return dict(tomlkit.parse(path.read_text(encoding="utf-8")))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _plan_text(path: Path) -> str:
|
|
33
|
+
plan = FlowPlan.load(path)
|
|
34
|
+
return " ".join(segment.text for unit in plan.flow for segment in unit.segments)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_compile_document_writes_default_layout_and_keeps_source_unchanged(
|
|
38
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
39
|
+
) -> None:
|
|
40
|
+
root = _workspace(tmp_path)
|
|
41
|
+
manifest = root / "manifest.json"
|
|
42
|
+
source = root / "document.ssmd.md"
|
|
43
|
+
manifest_before = manifest.read_bytes()
|
|
44
|
+
source_before = source.read_bytes()
|
|
45
|
+
|
|
46
|
+
result = main(
|
|
47
|
+
["compile-document", str(root), "--text-preparation", "identity", "--spacy", "off"]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
captured = capsys.readouterr()
|
|
51
|
+
output_dir = root / ".readio" / "utterplan"
|
|
52
|
+
plan_path = output_dir / "document-0001.utterplan.toml"
|
|
53
|
+
report_path = output_dir / "compile-report.toml"
|
|
54
|
+
assert result == 0
|
|
55
|
+
assert captured.out == ""
|
|
56
|
+
assert "UtterPlan document compile" in captured.err
|
|
57
|
+
assert f"source: {source}" in captured.err
|
|
58
|
+
assert "[1/1] document-0001" in captured.err
|
|
59
|
+
assert "written: 1" in captured.err
|
|
60
|
+
assert "Original content." in _plan_text(plan_path)
|
|
61
|
+
report = _report(report_path)
|
|
62
|
+
assert report["requested"] == 1
|
|
63
|
+
assert report["written"] == 1
|
|
64
|
+
assert report["item"][0]["id"] == "document-0001"
|
|
65
|
+
assert manifest.read_bytes() == manifest_before
|
|
66
|
+
assert source.read_bytes() == source_before
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_compile_document_uses_manifest_not_directory_suffix(
|
|
70
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
71
|
+
) -> None:
|
|
72
|
+
root = _workspace(tmp_path, "workspace")
|
|
73
|
+
|
|
74
|
+
result = main(
|
|
75
|
+
["compile-document", str(root), "--text-preparation", "identity", "--spacy", "off"]
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
assert result == 0
|
|
79
|
+
capsys.readouterr()
|
|
80
|
+
assert (root / ".readio/utterplan/document-0001.utterplan.toml").is_file()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_compile_document_uses_dirty_current_content_without_refreshing_manifest(
|
|
84
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
85
|
+
) -> None:
|
|
86
|
+
root = _workspace(tmp_path)
|
|
87
|
+
manifest = root / "manifest.json"
|
|
88
|
+
manifest_before = manifest.read_bytes()
|
|
89
|
+
source = root / "document.ssmd.md"
|
|
90
|
+
source.write_text(
|
|
91
|
+
'---\nssmd_version: "0.9"\nlanguage: en-US\n---\n\nEdited current content.\n',
|
|
92
|
+
encoding="utf-8",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
result = main(
|
|
96
|
+
["compile-document", str(root), "--text-preparation", "identity", "--spacy", "off"]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
assert result == 0
|
|
100
|
+
capsys.readouterr()
|
|
101
|
+
assert "Edited current content." in _plan_text(
|
|
102
|
+
root / ".readio/utterplan/document-0001.utterplan.toml"
|
|
103
|
+
)
|
|
104
|
+
assert manifest.read_bytes() == manifest_before
|
|
105
|
+
assert "Edited current content." in source.read_text(encoding="utf-8")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_compile_document_missing_source_is_a_reported_read_failure(
|
|
109
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
110
|
+
) -> None:
|
|
111
|
+
root = _workspace(tmp_path)
|
|
112
|
+
(root / "document.ssmd.md").unlink()
|
|
113
|
+
|
|
114
|
+
result = main(["compile-document", str(root)])
|
|
115
|
+
|
|
116
|
+
captured = capsys.readouterr()
|
|
117
|
+
report = _report(root / ".readio/utterplan/compile-report.toml")
|
|
118
|
+
assert result == 1
|
|
119
|
+
assert "FAILED" in captured.err
|
|
120
|
+
assert report["item"][0]["status"] == "failed"
|
|
121
|
+
assert report["item"][0]["stage"] == "read"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_compile_document_invalid_workspace_fails_before_output_creation(
|
|
125
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
126
|
+
) -> None:
|
|
127
|
+
root = _workspace(tmp_path)
|
|
128
|
+
manifest = root / "manifest.json"
|
|
129
|
+
data = json.loads(manifest.read_text(encoding="utf-8"))
|
|
130
|
+
data["format"] = "ssmdconvert.book"
|
|
131
|
+
manifest.write_text(json.dumps(data), encoding="utf-8")
|
|
132
|
+
output_dir = tmp_path / "outside-plans"
|
|
133
|
+
|
|
134
|
+
result = main(["compile-document", str(root), "--output-dir", str(output_dir)])
|
|
135
|
+
|
|
136
|
+
captured = capsys.readouterr()
|
|
137
|
+
assert result == 2
|
|
138
|
+
assert "ssmdconvert.document" in captured.err
|
|
139
|
+
assert not output_dir.exists()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_compile_document_output_and_report_overrides(
|
|
143
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
144
|
+
) -> None:
|
|
145
|
+
root = _workspace(tmp_path)
|
|
146
|
+
output_dir = tmp_path / "build" / "plans"
|
|
147
|
+
report = tmp_path / "reports" / "compile.toml"
|
|
148
|
+
|
|
149
|
+
result = main(
|
|
150
|
+
[
|
|
151
|
+
"compile-document",
|
|
152
|
+
str(root),
|
|
153
|
+
"--output-dir",
|
|
154
|
+
str(output_dir),
|
|
155
|
+
"--report",
|
|
156
|
+
str(report),
|
|
157
|
+
"--text-preparation",
|
|
158
|
+
"identity",
|
|
159
|
+
"--spacy",
|
|
160
|
+
"off",
|
|
161
|
+
]
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
assert result == 0
|
|
165
|
+
captured = capsys.readouterr()
|
|
166
|
+
assert str(output_dir) in captured.err
|
|
167
|
+
assert (output_dir / "document-0001.utterplan.toml").is_file()
|
|
168
|
+
assert report.is_file()
|
|
169
|
+
assert not (root / ".readio/utterplan").exists()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_compile_document_force_only_replaces_existing_plan(
|
|
173
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
174
|
+
) -> None:
|
|
175
|
+
root = _workspace(tmp_path)
|
|
176
|
+
output = root / ".readio/utterplan/document-0001.utterplan.toml"
|
|
177
|
+
output.parent.mkdir(parents=True)
|
|
178
|
+
output.write_text("sentinel", encoding="utf-8")
|
|
179
|
+
args = ["compile-document", str(root), "--text-preparation", "identity", "--spacy", "off"]
|
|
180
|
+
|
|
181
|
+
assert main(args) == 1
|
|
182
|
+
capsys.readouterr()
|
|
183
|
+
assert output.read_text(encoding="utf-8") == "sentinel"
|
|
184
|
+
|
|
185
|
+
assert main([*args, "--force"]) == 0
|
|
186
|
+
captured = capsys.readouterr()
|
|
187
|
+
assert "Original content." in _plan_text(output)
|
|
188
|
+
assert "written: 1" in captured.err
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_compile_document_accepts_canonical_document_file(
|
|
192
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
193
|
+
) -> None:
|
|
194
|
+
root = _workspace(tmp_path)
|
|
195
|
+
|
|
196
|
+
result = main(
|
|
197
|
+
[
|
|
198
|
+
"compile-document",
|
|
199
|
+
str(root / "document.ssmd.md"),
|
|
200
|
+
"--text-preparation",
|
|
201
|
+
"identity",
|
|
202
|
+
"--spacy",
|
|
203
|
+
"off",
|
|
204
|
+
]
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
assert result == 0
|
|
208
|
+
capsys.readouterr()
|
|
209
|
+
assert (root / ".readio/utterplan/document-0001.utterplan.toml").is_file()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def test_compile_document_uses_only_ssmd_language_or_explicit_fallback(
|
|
213
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
214
|
+
) -> None:
|
|
215
|
+
root = _workspace(tmp_path)
|
|
216
|
+
source = root / "document.ssmd.md"
|
|
217
|
+
source.write_text('---\nssmd_version: "0.9"\n---\n\nNo language.\n', encoding="utf-8")
|
|
218
|
+
args = ["compile-document", str(root), "--text-preparation", "identity", "--spacy", "off"]
|
|
219
|
+
|
|
220
|
+
assert main(args) == 1
|
|
221
|
+
capsys.readouterr()
|
|
222
|
+
item = _report(root / ".readio/utterplan/compile-report.toml")["item"][0]
|
|
223
|
+
assert item["code"] == "input.language_required"
|
|
224
|
+
|
|
225
|
+
assert main([*args, "--language", "en-US"]) == 0
|
|
226
|
+
capsys.readouterr()
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def test_compile_document_parser_exposes_only_single_document_options() -> None:
|
|
230
|
+
parser = build_parser()
|
|
231
|
+
args = parser.parse_args(
|
|
232
|
+
[
|
|
233
|
+
"compile-document",
|
|
234
|
+
"story.ssmdoc",
|
|
235
|
+
"--language",
|
|
236
|
+
"en-US",
|
|
237
|
+
"--text-preparation",
|
|
238
|
+
"identity",
|
|
239
|
+
"--renderability",
|
|
240
|
+
"strict",
|
|
241
|
+
"--spacy",
|
|
242
|
+
"off",
|
|
243
|
+
"--force",
|
|
244
|
+
"--diagnostics",
|
|
245
|
+
"none",
|
|
246
|
+
]
|
|
247
|
+
)
|
|
248
|
+
assert args.language == "en-US"
|
|
249
|
+
assert args.text_preparation == "identity"
|
|
250
|
+
assert args.renderability == "strict"
|
|
251
|
+
assert args.spacy == "off"
|
|
252
|
+
assert args.force is True
|
|
253
|
+
assert args.diagnostics == "none"
|
|
254
|
+
assert args.output_dir is None
|
|
255
|
+
with pytest.raises(SystemExit):
|
|
256
|
+
parser.parse_args(["compile-document", "story.ssmdoc", "--chapters", "1"])
|
|
257
|
+
with pytest.raises(SystemExit):
|
|
258
|
+
parser.parse_args(["compile-document", "story.ssmdoc", "--fail-fast"])
|
|
259
|
+
with pytest.raises(SystemExit):
|
|
260
|
+
parser.parse_args(["compile-document", "story.ssmdoc", "--input-format", "ssmd"])
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def test_public_workspace_api_does_not_shadow_core_compile_document() -> None:
|
|
264
|
+
from utterplan import (
|
|
265
|
+
DocumentCompileRequest,
|
|
266
|
+
DocumentCompileResult,
|
|
267
|
+
DocumentCompileStart,
|
|
268
|
+
DocumentWorkspaceServiceError,
|
|
269
|
+
compile_document,
|
|
270
|
+
compile_ssmdoc,
|
|
271
|
+
)
|
|
272
|
+
from utterplan.compiler import compile_document as compile_document_core
|
|
273
|
+
|
|
274
|
+
assert DocumentCompileRequest is not None
|
|
275
|
+
assert DocumentCompileResult is not None
|
|
276
|
+
assert DocumentCompileStart is not None
|
|
277
|
+
assert DocumentWorkspaceServiceError is not None
|
|
278
|
+
assert callable(compile_ssmdoc)
|
|
279
|
+
assert compile_document is compile_document_core
|