Compact semantic projection: model-text v2

September 13, 2026 · View on GitHub

Status: additive local implementation. Version 1 text and binary bytes remain unchanged. Model-text uses format version 2 and an explicit model-text encoding selection; it does not replace full JSON. Audience: agent and tool authors using model-text encoding and compiler contributors.

Wire and deterministic selection

The UTF-8 envelope begins SEMAPRAX-MODEL-TEXT 2\n, followed by the same four length-framed metadata fields as v1, in order: profile, root, source_revision, source_digest. Each field is name byte_length value\n. Next comes dict N\n, exactly N raw JSON string literals each followed by LF, and body\n followed by the selected JSON bytes with dictionary references. The digest uses v1’s existing domain-separated SHA-256 over the source length and exact reconstructed selected bytes.

For a selected document smaller than 16,384 bytes, the dictionary is empty. Otherwise, only JSON string literals at least 16 bytes long, including quotes and escape spelling, that occur at least twice enter the dictionary. Entries are sorted by their exact UTF-8 bytes and numbered from zero. The body replaces these literals with @ followed by the canonical unsigned decimal index, without a closing marker. Quotes delimit remaining inline strings, so @ and ~ inside them remain literal content. Short or infrequent strings remain inline. Selection is a fixed deterministic heuristic; no tokenizer, network, or model is consulted during encoding.

Validation and replay

encode_model_text consumes an opaque existing CompactProjection. decode_model_text parses the envelope independently, expands references with source bounds checked before appending, verifies the selected-byte digest, and normalizes through the existing projection encoder. Re-encoding must reproduce the entire v2 wire exactly, which rejects alternate dictionary order, duplicate entries, unnecessary references and noncanonical number or header spellings. decode_model_text_and_verify also verifies the expected profile, root and source revision with the existing binding diagnostic. Integrity is not origin authentication: the CLI replay route additionally regenerates the selected producer and requires exact selected-content equality.

Existing limits apply: 16 MiB wire and reconstructed source, 65,536 dictionary entries, 1 MiB per entry and 4 KiB per metadata field. Decoder errors use the existing compact diagnostic family; v1 compatibility and error meanings remain unchanged. Decoded v2 values can be serialized as ordinary v1 projections.

Interfaces and measurement

All six existing selected profiles admit --encoding model-text in the CLI and encoding: "model-text" in workspace/compact-projection, including its MCP forwarding route. The service reports format_version: 2. Negotiation requires the exact version/encoding/profile intersection; v1 text or binary cannot negotiate as v2. Existing binary-before-text preference is retained, with model-text following those encodings when multiple offers are common.

scripts/benchmark_compact_projection.py measures full JSON, v1 text and v2 model-text with the same cached cl100k/o200k tokenizers, while independently replaying all three wire forms including binary. The complete envelope counts toward measurements. Small inputs can grow because metadata has a fixed cost; this format does not claim universal savings, exact token budgets, or billing authority. The benchmark records exact bytes and hashes, tokenizer versions and vocabulary fingerprints. Results are local, not a hosted/provider result.

Local measurements

The committed measurement report uses cached tiktoken 0.12.0 with cl100k_base and o200k_base. Every wire replay matches the selected full bytes; graph cases also match the ordinary graph producer. Counts include the complete envelope.

Selected viewFull bytesModel-text bytescl100k full → model-texto200k full → model-text
examples/banking_ledger.spx139,770104,57937,846 → 32,48638,779 → 33,052
Task context: ledger.apply6,0696,3221,679 → 1,8021,721 → 1,844
examples/http_app_routing.spx610,864440,692161,861 → 134,165165,329 → 135,959
Task context: app.main19,79315,9745,602 → 5,0105,718 → 5,080
examples/calculator-project/semaprax.toml9,89310,1382,500 → 2,6152,521 → 2,639
examples/frame-payload-project/semaprax.toml6,6156,8601,766 → 1,8861,784 → 1,905

The two large graphs save 14–18% of measured tokens; the HTTP task context saves 11%. Small Project graphs and the banking task context grow 5–7% because the envelope dominates. Consumers should use full JSON for those small views when token cost is the priority. Version 1’s measured token regression remains documented; byte savings alone do not imply token savings.