# ADR-0004: Embedded Templates via `include_str!`

## Status

✅ Accepted

## Context

omni-dev ships as a single binary and uses several template files at runtime: Markdown
command templates for Claude Code, default commit guidelines for AI prompt construction, and
a YAML model registry for API parameter resolution. These files need to be available
wherever the binary runs, without requiring a separate installation step or a known
filesystem layout.

There are three broad strategies for making template content available at runtime:

- **Compile-time embedding** (`include_str!`) — the Rust compiler injects file contents into
  the binary as `&'static str` constants. The templates are versioned in lockstep with the
  code and can never be missing at runtime. The trade-off is that any template change
  requires recompilation, and the binary grows with the total size of embedded content.

- **Runtime file loading** — templates are read from disk at a known path (e.g. an XDG data
  directory or a path relative to the executable). This allows users to customise templates
  without recompiling but introduces failure modes: missing files, permission errors, version
  skew between binary and templates.

- **Separate config package** — templates are distributed as a companion artifact (e.g. a
  second crate, a tarball, or a package manager dependency). This adds distribution
  complexity and a version-synchronisation burden.

## Decision

We will embed all template files at compile time using `include_str!`. Each template is
embedded exactly once as a named constant and shared across all call sites that need it.

### Embedded templates

Five template files in `src/templates/` are embedded across three modules:

| Constant                   | Module              | Template                       | Category  |
|----------------------------|---------------------|--------------------------------|-----------|
| `COMMIT_TWIDDLE_TEMPLATE`  | `cli::commands`     | `commit-twiddle.md`            | Scaffold  |
| `PR_CREATE_TEMPLATE`       | `cli::commands`     | `pr-create.md`                 | Scaffold  |
| `PR_UPDATE_TEMPLATE`       | `cli::commands`     | `pr-update.md`                 | Scaffold  |
| `DEFAULT_COMMIT_GUIDELINES`| `claude::prompts`   | `default-commit-guidelines.md` | Prompt    |
| `MODELS_YAML`              | `claude::model_config` | `models.yaml`               | Config    |

### Three usage categories

The embedded templates serve three distinct purposes, each with different runtime behaviour:

1. **Scaffold generation** (`cli::commands`) — command templates are embedded so the binary
   can materialise them on disk via `omni-dev commands generate`. The binary acts as a
   portable distribution vehicle for these files. Once written, users may edit the generated
   files freely.

2. **Prompt construction** (`claude::prompts`) — `DEFAULT_COMMIT_GUIDELINES` is spliced into
   AI prompts at runtime as a fallback when no project-specific `commit-guidelines.md` is
   present. It never touches the filesystem.

3. **Structured configuration** (`claude::model_config`) — `MODELS_YAML` is deserialised
   into a typed `ModelRegistry` stored in a `static OnceLock`. The compile-time embedding
   guarantee justifies using `.expect()` on the parse result — if the YAML is malformed, the
   build itself is effectively broken. The same constant is referenced by `cli::config` for
   the `config models show` command.

### Single-source principle

Each template is embedded via `include_str!` exactly once and exposed as a module-level
constant. Other modules that need the same content import the constant rather than embedding
the file a second time. This prevents binary bloat from duplicate embeddings and ensures a
single source of truth.

## Consequences

**Positive:**

- **Single-binary distribution.** No external file dependencies, no installer, no XDG path
  resolution. `cargo install omni-dev` or a downloaded binary is fully self-contained.
- **No runtime file-not-found errors.** If the binary compiles, the templates are present.
  This eliminates an entire class of deployment failures.
- **Version coherence.** Templates are always in sync with the code that consumes them.
  There is no possibility of a stale `models.yaml` being loaded from a previous installation.
- **Compile-time validation.** The `ModelRegistry` parse in tests exercises the embedded YAML
  on every `cargo test` run, catching schema drift immediately.

**Negative:**

- **Template changes require recompilation.** Editing a template and seeing the result
  requires `cargo build`. This is the normal Rust development workflow but means end users
  cannot patch templates in place (with one exception — see below).
- **Binary size grows with template count.** The current templates total ~19 KB, which is
  negligible. This would need revisiting if large assets (images, full documentation) were
  added.

**Neutral:**

- **Commit guidelines have a runtime override path.** `claude::prompts` only falls back to
  `DEFAULT_COMMIT_GUIDELINES` when `context.project.commit_guidelines` is `None`. Projects
  can provide their own `.omni-dev/commit-guidelines.md` to override the embedded default
  (see [`omni-dev-directory.md`](../omni-dev-directory.md#commit-guidelinesmd) for the
  current format and precedence contract). No other template type currently has an equivalent
  override mechanism. This asymmetry is intentional: command templates are already written
  to disk (where users can edit them), and the model registry is a versioned data structure
  that should match the binary's expectations.
