Philosophy
Core design principles for Paraphase.
Paraphase is a Pipeline Orchestrator
Founding use case: Game asset conversion - textures, meshes, audio, configs all need processing through diverse tools with inconsistent interfaces.
Core job: Unification. One vocabulary, one interface, many backends.
The Agent Knowledge Gap
The deeper motivation: agents suck at toolchains.
When converting data programmatically, you face:
- "Do I run blender? gltf-pipeline? meshoptimizer?"
- "What flags? Is it installed? Which version?"
- Hallucinated flags, wrong CLI versions, failed optimizations
Paraphase solves this by being a route planner, not a task runner.
# Task runner (make/just): agent must know the recipe
blender --background --python export.py -- input.blend output.glb
gltf-pipeline -i output.glb -o optimized.glb --draco.compressionLevel 7
# Paraphase: agent only knows source and destination
paraphase convert model.blend optimized.glb --optimizeThe agent says "I have X, I need Y" - paraphase finds the path through the graph.
Why not existing tools?
| Tool | Approach | Gap |
|---|---|---|
| Make | File-based, mtime-driven | You write the recipes |
| Just | Task runner | You write the recipes manually |
| Nix | Content-addressed, reproducible | Heavyweight, config-heavy |
| Paraphase | Type-driven route planning | Agent just declares intent |
Scope test: If the transformation is "agent shouldn't need to know the toolchain," it's in scope. If it requires business logic or architectural decisions, it's out.
Plan → Execute (Agent-Friendly Interface)
Conversions are two-phase:
Phase 1: Plan - Find path, surface required decisions.
paraphase plan --from sprites/*.png --to spritesheet.png
# Output:
# Suggested path: glob → regex-extract → spritesheet-pack
# Required:
# regex-extract.pattern: string (regex with named groups)
# Optional:
# spritesheet-pack.quality: 0-100 (default: 80)
# spritesheet-pack.padding: int (default: 2)
# Presets: --preset lossless | balanced | crush
# Tools: all available ✓Phase 2: Execute - Provide options, run the path.
paraphase convert sprites/*.png spritesheet.png \
--pattern "sprite_(?<id>\d+)_(?<frame>\d+)" \
--preset balancedIncomplete plans = suggestions. No separate suggest command.
# Incomplete: only source and sink
paraphase plan --from input.png --to output.webp
# Paraphase suggests pipeline + shows what options are available
# Complete: shows exact execution plan
paraphase plan workflow.yamlNormalized Options
Users learn ONE vocabulary. Paraphase maps to tool-specific flags:
# Same --quality flag everywhere
paraphase convert image.png image.webp --quality 80 # → cwebp -q 80
paraphase convert video.mp4 video.webm --quality 80 # → ffmpeg -crf 23
paraphase convert model.glb model.glb --quality 80 # → draco level 7Agent doesn't need to know that quality=80 means different flags for different tools.
Presets
Declarative option bundles for common scenarios:
# presets.toml
[lossless]
quality = 100
compression = "lossless"
[balanced]
quality = 80
compression = "lossy"
[crush]
quality = 60
strip_metadata = trueparaphase convert image.png image.webp --preset crush
paraphase convert image.png image.webp --preset balanced --quality 90 # overrideProperty Bags, Not Types
See ADR-0003
Data is described by property bags, not hierarchical types:
{format: "png", width: 1024, height: 768, colorspace: "srgb"}Conversion = property transformation. Format is just another property.
{format: "png", width: 4096} → {format: "webp", width: 1024}Same model handles format change, resize, transcode, etc.
Property naming: Flat by default, namespace only when semantics differ.
width,height,format- universalimage.compressionvsarchive.compression- different meanings
Workflows
Workflows are serializable pipelines:
# workflow.yaml
preset: balanced
source:
glob: "sprites/*.png"
steps:
- converter: regex-extract
options:
pattern: "sprite_(?<id>\\d+)_(?<frame>\\d+)"
- converter: spritesheet-pack
options:
quality: 90
sink:
path: "output/sprites.png"Format-agnostic: YAML, TOML, JSON - paraphase eats its own dogfood.
paraphase convert workflow.json workflow.yaml # convert workflow files tooAgents can build workflows programmatically:
Workflow::new()
.preset("balanced")
.source("sprites/*.png")
.pipe("regex-extract", [("pattern", r"sprite_(?<id>\d+)_(?<frame>\d+)")])
.pipe("spritesheet-pack", [("quality", 90)])
.sink("output/sprites.png")
.build()?N→M Cardinality
Not all conversions are 1→1:
| Pattern | Example |
|---|---|
| 1→1 | png → webp |
| 1→N | video → frames |
| N→1 | frames → video, files → manifest |
| N→M | batch tree conversion |
Converters declare their cardinality. Orchestration handles batching.
No special cases: sidecars, manifests, spritesheets are all just N→M conversions.
Pattern Extraction (Plugin)
Structured filename parsing is a plugin, not core. Uses regex:
paraphase convert "sprites/*.png" spritesheet.png \
--pattern "sprite_(?<id>\d+)_(?<frame>\d+)"The regex-extract plugin parses filenames, enriches properties:
- Input:
{path: "sprite_001_002.png"} - Output:
{path: "sprite_001_002.png", id: "001", frame: "002"}
Why regex: agents know regex (even if imperfectly), no new DSL to learn.
Plugins, Not Monolith
Unlike pandoc/ffmpeg (which bundle everything), Paraphase is:
- Core: property bags, graph traversal, workflow orchestration, CLI
- Plugins: converters, inspectors, pattern extractors
paraphase plugin add paraphase-images # png, jpg, webp, etc.
paraphase plugin add paraphase-ffmpeg # video/audio via ffmpeg
paraphase plugin add paraphase-regex # pattern extractionPlugins are C ABI dynamic libraries. See ADR-0001.
Hand-Rolled Formats Are Standalone Crates
When Paraphase rolls its own parser/writer for a format, the implementation lives in a standalone crate (e.g. amazon-ion, woff, subtitle-formats) that is useful independently of Paraphase. The paraphase-* crate is a thin wrapper that registers converters against the Paraphase registry.
Rationale: other projects (e.g. rescribe) may need the same format support. They should depend on the standalone crate, not on Paraphase.
The pattern:
amazon-ion ← general-purpose Ion parser/writer (no Paraphase dependency)
└── paraphase-ion ← thin wrapper: registers Ion converters with the registryThis applies to every hand-rolled format. When wrapping an existing ecosystem crate (e.g. gpx, pem-rfc7468) there is no standalone crate needed — the ecosystem crate already serves that role.
Standalone crates should prefer non-destructive parsing — preserve structure and formatting when round-tripping, only touching what was explicitly changed. This is also rescribe's core design principle (and its main advantage over Pandoc). Alignment here is by design, not coincidence: the same philosophy should flow through the whole stack.
An IR is the natural consequence of combinatorics, not of losslessness. Without an IR you need N×M converters; with one you need N parsers + M serializers. Losslessness is a separate, orthogonal property — it depends on how expressive the IR is, not on whether one exists. subparse has SubtitleEntry, boko has a node/style IR, rescribe has its own document IR — all arrived at the same structure for the same reason.
Library-First
Paraphase is a library with a CLI wrapper, not vice versa.
use paraphase::{Registry, Workflow};
let registry = Registry::with_default_plugins()?;
let plan = registry.plan(&from_props, &to_props)?;
let result = registry.execute(&plan, &input, &options)?;See ADR-0002.
No Special Cases
Design principle: if something feels like a special case, generalize it.
- "Sidecars" → just 1→N conversion
- "Manifests" → just N→1 conversion
- "Presets" → just option bundles
- "Pattern extraction" → just a property-enriching converter
- "Suggest" → just
planon incomplete workflow
One model, many uses.
Prior Art & Inspiration
Tools that informed Paraphase's design:
| Tool | What It Does | What We Take |
|---|---|---|
| CyberChef | "Cyber Swiss Army Knife" - browser-based encoding, compression, hashing, data analysis | Recipe-based pipelines, comprehensive format coverage, "bake" metaphor |
| Pandoc | Universal document converter | Format graph traversal, intermediate representation idea |
| FFmpeg | Media transcoding | Filter graphs, format negotiation |
| ImageMagick | Image manipulation | Batch processing, format detection |
| jq | JSON processor | Streaming, composable transformations |
Key difference: These tools are format-specific or monolithic. Paraphase is a unified orchestrator - one interface that routes to the right tool for each conversion.