Skip to content

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.

bash
# 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 --optimize

The agent says "I have X, I need Y" - paraphase finds the path through the graph.

Why not existing tools?

ToolApproachGap
MakeFile-based, mtime-drivenYou write the recipes
JustTask runnerYou write the recipes manually
NixContent-addressed, reproducibleHeavyweight, config-heavy
ParaphaseType-driven route planningAgent 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.

bash
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.

bash
paraphase convert sprites/*.png spritesheet.png \
    --pattern "sprite_(?<id>\d+)_(?<frame>\d+)" \
    --preset balanced

Incomplete plans = suggestions. No separate suggest command.

bash
# 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.yaml

Normalized Options ​

Users learn ONE vocabulary. Paraphase maps to tool-specific flags:

bash
# 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 7

Agent doesn't need to know that quality=80 means different flags for different tools.

Presets ​

Declarative option bundles for common scenarios:

toml
# presets.toml
[lossless]
quality = 100
compression = "lossless"

[balanced]
quality = 80
compression = "lossy"

[crush]
quality = 60
strip_metadata = true
bash
paraphase convert image.png image.webp --preset crush
paraphase convert image.png image.webp --preset balanced --quality 90  # override

Property 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 - universal
  • image.compression vs archive.compression - different meanings

Workflows ​

Workflows are serializable pipelines:

yaml
# 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.

bash
paraphase convert workflow.json workflow.yaml  # convert workflow files too

Agents can build workflows programmatically:

rust
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:

PatternExample
1→1png → webp
1→Nvideo → frames
N→1frames → video, files → manifest
N→Mbatch 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:

bash
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
bash
paraphase plugin add paraphase-images   # png, jpg, webp, etc.
paraphase plugin add paraphase-ffmpeg   # video/audio via ffmpeg
paraphase plugin add paraphase-regex    # pattern extraction

Plugins 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 registry

This 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.

rust
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 plan on incomplete workflow

One model, many uses.

Prior Art & Inspiration ​

Tools that informed Paraphase's design:

ToolWhat It DoesWhat We Take
CyberChef"Cyber Swiss Army Knife" - browser-based encoding, compression, hashing, data analysisRecipe-based pipelines, comprehensive format coverage, "bake" metaphor
PandocUniversal document converterFormat graph traversal, intermediate representation idea
FFmpegMedia transcodingFilter graphs, format negotiation
ImageMagickImage manipulationBatch processing, format detection
jqJSON processorStreaming, 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.