renderplan MVP
renderplan is a deliberately small Python contract package shared by UtterPlan, VoiceRender, and AudioCompose. It contains no SSMD compiler, NLP, TTS engine, G2P, waveform, DSP, or orchestration implementation.
The intended dependency graph is:
utterplan ──────┐
voicerender ────┼──> renderplan
audiocompose ──┘
The applications do not depend on each other; the contract package is their only shared dependency.
Contracts
The package defines four TOML documents:
renderprofile.toml— seeded by VoiceRender and user-owned. It contains opaque, self-contained renderer bindings, portable requirements, optional capacity descriptions, split/fallback policy, and explicit pause durations. Shared pause modes aremanualandnone; orchestration pass limits do not belong in this contract.renderplan.toml— produced by UtterPlan. Its ordered flow is the exact serial execution contract: numbered speech requests, exactNNNNNN.wavnames, generic pauses, audio items, controls, tokens, source provenance, and role bindings copied from the profile.renderplan-preflight.toml— produced by VoiceRender for one exact RenderPlan. It has statusok,replan, orrejectand refers to speech occurrences by(segment_number, segment_id).render-manifest.toml— produced by VoiceRender. It binds WAV metadata to exact(segment_number, segment_id)plan occurrences. A complete manifest contains exactly every plan speech occurrence; an incomplete one may contain only occurrences from that plan.
Consumers can use the shared joins instead of implementing their own parsers:
plan.validate_against_profile(profile)
report.validate_against(plan)
manifest.validate_against(plan)
These are portable contract checks only; they do not resolve engines, inspect WAV files, or orchestrate a pipeline.
UtterPlan v7 source and token mapping
The first producer uses zero-based v7 provenance locators such as f000000.s000, f000000.s001, and f000001.s000: six digits for the flow index and three for the segment index. A locator is an opaque provenance address, meaningful only together with RenderPlan.source_plan_id; it is not a globally stable segment ID. renderplan does not parse or resolve locators and has no UtterPlan dependency.
SourceSpan.start and .end are Python string/code-point offsets into the prepared FlowSegment.text. For every speech request, UtterPlan must prove:
SpeechItem.text == concatenation, in order, of each referenced FlowSegment.text[start:end]
Do not trim request-edge whitespace. These offsets also map source spans and request-local controls/tokens deterministically. Source-to-text equality is proved by UtterPlan against its source document, not by renderplan.
The destination start offset of source span N inside the request is the sum of the lengths of all preceding source slices. This preserves a direct mapping without trimming or a second offset table.
TokenBundle.span is a dense, ordered list of request-local [start, end] character ranges; pos and tag, when present, are dense parallel arrays. lemma and morph are sparse arrays of [token_index, value]. VoiceRender derives a token surface from speech.text[start:end]. Profile-required token fields must be complete under those semantics; available optional fields should be preserved, but lemma and morph should not be declared required unless an adapter genuinely requires them.
SpeechControl is request-local and renderer-neutral: it carries start, end, name, and TOML-safe params. Initial semantic names include pronunciation, prosody, emphasis, say_as, and extension:<name>. Controls participate in segment_id; renderers decide support, and unsupported controls must fail preflight rather than be ignored. Control ranges may overlap only when the source semantics permit it. Engine-specific voice selection is resolved into the opaque logical role/binding; audio directives become AudioItems.
Bindings copied into plans must be self-contained enough to execute without reopening the RenderProfile. This contract does not define renderer-specific path keys or local-asset resolution. This schema has no generic asset-reference type yet. A VoiceRender profile producer must refuse bindings that depend on profile-local or otherwise uncopied asset state; do not introduce renderer-specific asset-path keys into this shared contract.
Identity and replan contract
segment_id identifies the exact synthesis request: text, language, role, binding, tokens, and controls. It excludes display number and plan provenance such as source spans, split reason, planning level, and semantic end. Duplicate requests may therefore share a segment_id; (segment_number, segment_id) identifies each occurrence within a plan, report, or manifest.
plan_id identifies the complete ordered execution flow, including speech numbers/order, request IDs, provenance and planning semantics, preflight requirements, every pause, and every audio item. Producer package versions do not change semantic profile or plan identity. Exact source-file bytes are not included in RenderPlan identity.
A PreflightReport describes failures only for its concrete RenderPlan; it cannot preserve successful capacity splits from earlier attempts. The replan join is therefore:
original UtterPlan + RenderProfile + previous RenderPlan
+ PreflightReport for that previous RenderPlan
-> new RenderPlan
UtterPlan uses the previous plan as the partition baseline and rebuilds only failed source ranges. Each replan request must correspond to a too_long check and exactly match that speech occurrence's source coverage and current planning level. VoiceRender preflight never edits the plan. replan is only for recoverable capacity failures; missing engines, unknown voices, unsupported controls, invalid token fields, and frontend/API failures are reject conditions.
A PauseItem is a generic serial event positioned by flow, with duration, reason, and optional provenance; it can lead a flow or follow speech, audio, or another operation. No preceding speech number is required. The ordered flow is authoritative for AudioCompose.
Example preflight occurrence
[[checks]]
segment_number = 37
segment_id = "sha256:..."
status = "too_long"
metric = "model_tokens"
measured = 532
maximum = 510
source = "pykokoro.prepare"
[[replan]]
segment_number = 37
segment_id = "sha256:..."
current_level = "sentence"
action = "split"
reason = "capacity"
metric = "model_tokens"
measured = 532
maximum = 510
[[replan.source]]
locator = "f000123.s004"
start = 0
end = 42
UtterPlan does not need to understand model_tokens; the source-plan ID, copied profile ID, exact plan ID, source ranges, and generic split level/action allow stale feedback to be rejected without importing VoiceRender.
Install for development
This repository intentionally has no src/ directory.
python -m pip install -e '.[dev]'
pytest
ruff check .
mypy renderplan
Versioning is dynamic through setuptools-scm; create Git tags such as v0.1.0. A source tree without Git metadata uses the configured development fallback version.
Example API
from renderplan import load_plan, load_preflight
plan = load_plan("chapter.renderplan.toml")
report = load_preflight("chapter.preflight.toml")
report.validate_against(plan)
if report.status == "replan":
for request in report.replan:
print(request.segment_number, request.action, request.source)
MVP invariants
- Persisted identities are SHA-256 over canonical semantic payloads; producer versions are provenance, not identity.
- Speech numbers are contiguous starting at 1; speech
Nmaps toNNNNNN.wav. - Speech-request identity is stable across renumbering; full plan identity changes when flow order or execution semantics change.
RenderPlan.validate_against_profile,PreflightReport.validate_against, andRenderManifest.validate_againstenforce portable joins and exact occurrence matching.- VoiceRender preflight never changes the plan, and rendering never introduces unplanned speech segments.
- AudioCompose consumes RenderPlan flow and RenderManifest artifacts; it does not reconstruct pauses from UtterPlan.
- Engine-specific binding tables are opaque to UtterPlan and AudioCompose.
Metadata
Release files for renderplan 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| renderplan-0.1.0.tar.gz | 47.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| renderplan-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 74.4 kB
Release files / renderplan-0.1.0.tar.gz
| Download URL | renderplan-0.1.0.tar.gz |
|---|---|
| Size | 47.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a4cc89cf89d493fea85d4521a3576a1100f0f670a4fb6713af39d3eb89f6c7ea
|
|
BLAKE2b-256 checksum How to use checksums |
a2c7836af3fb2c448b7057dfa637470c5fc9a3c8c72b728e801a0bb63261656e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Release files / renderplan-0.1.0-py3-none-any.whl
| Download URL | renderplan-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f8522716f505771fc34288ac433736af937c610abb922a4d137a0ce5c2a1309
|
|
BLAKE2b-256 checksum How to use checksums |
507bb6dba564f6e1f8683f4b09c098f4f96c78c7b359a2baa4997e6b460cb15e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|