Extensions
Add refined variants alongside the base cross product.
extensions add refined variants on top of the base cross product when the base axes do not express every combination you need. Use extensions to add a slice with different axis values, swap products or environments for a subtree, or append prompt guidance to a slice of variants, without touching the base matrix.
Extensions and the base matrix
extensions are additive: Fiveonefour resolves the base cross product from the top-level axes, then resolves each extensions subtree and adds those variants alongside it. Extensions never remove or replace base variants.
The base cross product is only emitted when the top level can form complete variants on its own, both agents and at least one prompts entry declared at the top level. If either axis is supplied only inside an extension, the top level has nothing to resolve by itself, so no bare base variant is emitted, only the extension-derived ones.
| Top level declares | Base variants emitted? |
|---|---|
agents and prompts | Yes, alongside every extension's variants |
agents only (no top-level prompts) | No, an extension must supply prompts |
prompts only (no top-level agents) | No, an extension must supply agents |
Adding a variant alongside the base matrix
Because extensions add to the base matrix, use a small extension to try one more agent or model without repeating the rest of the experiment:
agents: [claude]
models: [claude-sonnet-4-6]
prompts:
- id: install
prompt: "Install the CLI and write its version to /workspace/version.txt."
extensions:
- id: also-codex
agents: codex
models: gpt-5.6-terraThis resolves the base claude × claude-sonnet-4-6 variant plus one extension-derived variant on codex × gpt-5.6-terra, both running the same prompt. No passthrough arm is needed to keep the base variant: it is emitted automatically.
Migrating a fork-style experiment
Before extensions were additive, an experiment that only wanted extension-derived variants put agents (or prompts) at the top level anyway, relying on extensions to suppress the bare base variant. That suppression is gone: the same file would now also emit an unwanted base variant.
To keep only the arms you intended, relocate agents down into each extension arm instead of leaving it at the top level. That leaves the top level incomplete on purpose, so no base variant is emitted, and because agents fully replaces that axis per subtree, each arm reproduces the exact same variant_id, prompt_id, resolved_extend_id, and variant-level fingerprints as before the migration. That preservation is per-variant, not per-experiment: any YAML edit, including this relocation, still changes the experiment-level fingerprint, so pushing a migrated file mints a new experiment version even though its variants keep their identities.
# Before: agents at the top level made the top level complete, so extensions
# used to suppress the (unwanted) base variant.
agents: claude
prompts:
- id: task
prompt: "Investigate the regression."
extensions:
- id: grep
products: [{ name: grep-only, setup: "true" }]
- id: rg
products: [{ name: ripgrep, setup: "install-ripgrep" }]# After: agents moved into each arm, so the top level has no agents and
# emits no base variant. Only the two arms resolve, with the same
# variant_id, prompt_id, and resolved_extend_id as before the migration.
prompts:
- id: task
prompt: "Investigate the regression."
extensions:
- id: grep
agents: claude
products: [{ name: grep-only, setup: "true" }]
- id: rg
agents: claude
products: [{ name: ripgrep, setup: "install-ripgrep" }]Relocating prompts instead of agents also makes the top level incomplete and also stops the base variant from emitting, but it does not preserve identity: with no top-level prompts, the prompt id collapses to a synthesized p0, the original prompt text moves entirely into prompt_suffixes (there is no base prompt left to append it to), and prompt_base is empty. That changes variant_id's prompt component and every prompt-dimension and variant fingerprint. Relocating agents avoids all of that, since the agents axis simply replaces per subtree and never touches how prompts are recorded.
Extension object
An extension object is one node in the refinement tree. Nested extensions are recursively cross-multiplied with their parents.
| Field | Required | Type / values | Notes |
|---|---|---|---|
id | Yes | Kebab-case string | Must be unique among sibling extensions. |
description | No | String | Human-readable notes. |
tags | No | String list | Added to resolved variant tags. |
agents | No | Agent list | Replaces inherited agents for this extension subtree. |
models | No | Model axis | Replaces inherited models for this extension subtree. |
agent_mode | No | Agent-mode axis | Replaces inherited agent modes for this extension subtree. |
prompts | No | Prompt list | An axis: each entry forks its own variant branch, and that entry's text is appended to inherited prompt text. Does not replace the prompt axis. |
environments | No | Environment list | Replaces inherited environments for this extension subtree. |
products | No | Product list | Replaces inherited products for this extension subtree. |
extensions | No | Extension list | Nested extensions. |
prompts:
- id: analyze
prompt: "Read /workspace/task.md and write /workspace/report.json."
extensions:
- id: with-cli
products:
- name: cli
type: CLI
setup: "curl -fsSL https://clickhouse.com/ | sh"
prompts: ["Use the ClickHouse CLI for the analysis."]
- id: without-cli
products:
- name: no-cli
setup: "true"
prompts: ["Do not use the ClickHouse CLI; use another local method."]Prompt forking
An extension's prompts list is an axis, exactly like the top-level prompt
list: each entry creates an independent variant branch, multiplied against the
other axes the extension accumulates. Only the chosen entry's text is appended
(after a blank line) to every inherited base prompt. The base prompt id is
preserved; in a multi-entry list each entry's id follows the extension id in the
variant id (bare strings get positional ids p0, p1, …). A single-entry list
appends its suffix without adding an id coordinate. Like the extension's chosen
environment and product, the entry id is a variant coordinate only;
resolved_extend_id stays the extension id path that both arms share.
agents: claude
prompts:
- id: base
prompt: BASE
extensions:
- id: scenario
environments: [{ name: h, setup: "true" }]
prompts:
- id: global-only
prompt: "Use only the global config."
- id: global-plus-scenario
prompt: |
Use only the global config.
Investigate the gap.This resolves to two variants: …::base::h::scenario::global-only (prompt
BASE + Use only the global config.) and …::base::h::scenario::global-plus-scenario
(prompt BASE + the two-line entry), alongside the base variant …::base (no
environments at the top level, so no h coordinate, and no extension
suffix).