Search documentation

Search the Fumadocs-backed documentation index.

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 declaresBase variants emitted?
agents and promptsYes, 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-terra

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

FieldRequiredType / valuesNotes
idYesKebab-case stringMust be unique among sibling extensions.
descriptionNoStringHuman-readable notes.
tagsNoString listAdded to resolved variant tags.
agentsNoAgent listReplaces inherited agents for this extension subtree.
modelsNoModel axisReplaces inherited models for this extension subtree.
agent_modeNoAgent-mode axisReplaces inherited agent modes for this extension subtree.
promptsNoPrompt listAn 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.
environmentsNoEnvironment listReplaces inherited environments for this extension subtree.
productsNoProduct listReplaces inherited products for this extension subtree.
extensionsNoExtension listNested 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).