Compilation Architecture

Understanding how PackC compiles prompts into packs.

Overview

PackC transforms human-friendly YAML configurations into optimized, validated JSON packs through a multi-stage compilation pipeline.

Compilation Pipeline

YAML Files → Parser → Validator → Optimizer → Pack Builder → JSON Output

Stage 1: Configuration Loading

Input: arena.yaml + prompt YAML files

# arena.yaml
prompts:
  - prompts/support.yaml
  - prompts/sales.yaml

Process:

  1. Read arena.yaml
  2. Resolve file paths (relative to arena.yaml)
  3. Load each prompt YAML
  4. Parse YAML to PromptConfig structs

Output: In-memory PromptConfig objects

Stage 2: Prompt Registry

Purpose: Central repository of all prompts

registry := prompt.NewRegistry()
registry.Register(promptConfig)

Features:

Stage 3: Validation

Checks:

Output: Validated PromptConfig objects or errors

Stage 4: Optimization

Transformations:

  1. Template compilation - Pre-parse templates
  2. Whitespace normalization - Consistent formatting
  3. Fragment resolution - Inline or reference
  4. Tool deduplication - Merge duplicate tools
  5. Metadata extraction - Build pack metadata

Stage 5: Pack Assembly

Process:

  1. Create pack structure
  2. Add each prompt to prompts map
  3. Add fragments map
  4. Add metadata (compiler version, timestamp)
  5. Assign pack ID and version

Output: Complete Pack object

Stage 6: JSON Serialization

Format: Indented JSON for readability

json.MarshalIndent(pack, "", "  ")

Options:

Output: .pack.json file

Compiler Components

PromptConfig Parser

Parses YAML to PromptConfig:

func ParsePromptConfig(data []byte) (*PromptConfig, error)

Handles:

Pack Compiler

Orchestrates compilation:

compiler := prompt.NewPackCompiler(registry)
pack, err := compiler.CompileFromRegistry(packID, compilerVersion)

Responsibilities:

Validator

Validates prompts and packs:

warnings := pack.Validate()

Returns list of validation warnings (non-fatal) or errors (fatal).

Template Processing

Go Templates

Default template engine:

user_template: |
  User: 
  Message: 

Processing:

  1. Parse template text
  2. Check syntax errors
  3. Store as string (runtime parsing)

Why not pre-compile?

Template Validation

PackC validates template syntax:

_, err := template.New("test").Parse(tmpl)

But doesn’t execute (no runtime data available).

Fragment Handling

Fragment Definition

# fragment.yaml
fragments:
  company-info:
    content: "Company: "
    description: "Standard company info"

Fragment Compilation

Two strategies:

1. Inline (default)

Fragment content embedded in prompt:

{
  "prompts": {
    "support": {
      "system": "Company: \n\nYou are support..."
    }
  }
}

2. Reference

Fragment stored separately:

{
  "fragments": {
    "company-info": { "content": "..." }
  },
  "prompts": {
    "support": {
      "system": "\n\nYou are support...",
      "fragments": ["company-info"]
    }
  }
}

Tradeoff:

Error Handling

Fatal Errors

Stop compilation immediately:

Warnings

Allow compilation but report issues:

Error Context

Provide helpful error messages:

Error parsing prompt config: yaml: line 5: mapping values are not allowed
File: prompts/support.yaml
Line: 5

Performance Optimizations

1. Concurrent Loading

Load multiple prompt files in parallel:

// Pseudocode
for each promptFile {
  go loadPrompt(promptFile)
}

2. Validation Caching

Cache validation results:

if cached := validationCache[promptID]; cached != nil {
  return cached
}

3. Incremental Compilation

Only recompile changed prompts (future):

if !hasChanged(promptFile) {
  return cachedPack
}

4. Streaming Output

Write pack JSON as generated:

encoder := json.NewEncoder(file)
encoder.Encode(pack)

Memory Management

Small Footprint

PackC uses minimal memory:

  1. Streaming YAML parsing - Process files one at a time
  2. Lazy loading - Load prompts on demand
  3. Garbage collection - Release after compilation
  4. No caching - Don’t hold data post-compile

Large Projects

For projects with 100+ prompts:

Compilation Modes

Standard Mode

packc compile --config arena.yaml --output pack.json --id app

Fast Mode (future)

packc compile --fast

Strict Mode (future)

packc compile --strict

Deterministic Builds

PackC produces deterministic output:

Given:

Result:

Implementation:

Compilation Hooks (future)

Allow customization:

compiler.AddHook("pre-validate", func(prompt *PromptConfig) error {
  // Custom validation
})

compiler.AddHook("post-compile", func(pack *Pack) error {
  // Custom transformations
})

Use cases:

Debugging Compilation

Verbose Output

packc compile --verbose

Shows:

Dry Run

packc compile --dry-run

Inspect Intermediate

View stages:

packc compile --dump-ast      # After parsing
packc compile --dump-validated  # After validation
packc compile --dump-optimized  # After optimization

Build Reproducibility

Version Locking

Lock packc version:

# .packc-version
0.1.0

Input Hashing

Track source file changes:

find prompts/ -type f -exec sha256sum {} \; > prompts.sha256

Build Manifest

Generate build info:

{
  "packc_version": "0.1.0",
  "source_files": ["prompts/support.yaml"],
  "file_hashes": {"prompts/support.yaml": "abc123..."},
  "build_time": "2025-01-16T10:30:00Z",
  "build_machine": "ci-runner-01"
}

Compiler Architecture

Modular Design

PackCompiler
├── Parser (YAML → PromptConfig)
├── Validator (checks)
├── Optimizer (transformations)
├── Assembler (build pack)
└── Serializer (write JSON)

Each component is:

Extension Points

  1. Custom parsers - Support other input formats
  2. Custom validators - Add validation rules
  3. Custom optimizers - Apply transformations
  4. Custom serializers - Output other formats

Comparison with Other Compilers

vs. TypeScript Compiler

FeaturePackCtsc
InputYAMLTypeScript
OutputJSONJavaScript
Type checkingLimitedFull
OptimizationMinimalExtensive
Speed~100ms~1-10s

vs. Babel

FeaturePackCBabel
InputYAMLJavaScript
OutputJSONJavaScript
TransformationsFewMany
PluginsPlannedExtensive
SpeedFastModerate

Future Enhancements

1. Incremental Compilation

Only recompile changed prompts:

# First build: compile all
packc compile

# Subsequent: only changed
packc compile --incremental

2. Watch Mode

Auto-recompile on file changes:

packc compile --watch

3. Parallel Compilation

Compile multiple packs in parallel:

packc compile config/*.yaml --parallel

4. Custom Output Formats

Support other formats:

packc compile --format yaml  # Output YAML pack
packc compile --format toml  # Output TOML pack

Summary

PackC’s compilation architecture is:

This design ensures reliable, consistent pack generation for production use.