goose icon indicating copy to clipboard operation
goose copied to clipboard

Desktop: Subrecipes not imported when using recipe deeplinks from web registry

Open angiejones opened this issue 1 month ago • 1 comments

Bug Description

When importing recipes from the web registry (https://block.github.io/goose/recipes) into Goose Desktop, subrecipes are not included in the deeplink, causing recipes with subrecipes to fail when executed.

Impact

Recipes that use subrecipes (e.g., ab-test-framework-generator.yaml, data-analysis-pipeline.yaml, etc.) cannot be successfully imported and run from the web registry in Desktop. Users get an error: Sub-recipe file does not exist.

Root Cause

The website's deeplink encoding in documentation/src/utils/recipes.ts only includes these fields:

const configForGoose = {
  title: cleaned.title,
  description: cleaned.description,
  instructions: cleaned.instructions,
  prompt: cleaned.prompt,
  activities: cleaned.activities,
  extensions: cleaned.extensions,
  parameters: (cleaned as any).parameters || []
};

The sub_recipes field is completely omitted from the encoded deeplink.

Scope

  • Desktop: ❌ Broken - subrecipes not included in deeplinks
  • CLI: ✅ Works correctly - uses git archive to download entire recipe directory including subrecipes

Steps to Reproduce

  1. Go to https://block.github.io/goose/recipes
  2. Find a recipe with subrecipes (e.g., "A/B Test Framework Generator")
  3. Click to import the recipe into Goose Desktop
  4. Try to run the recipe
  5. Error: Sub-recipe file does not exist: ./subrecipes/experiment-tracker.yaml

Expected Behavior

When importing a recipe with subrecipes, all referenced subrecipe files should be available for execution.

Affected Recipes

At least 6 recipes in the registry use subrecipes:

  • ab-test-framework-generator.yaml
  • data-analysis-pipeline.yaml
  • openapi-to-locust.yaml
  • security-audit-pipeline.yaml
  • technical-debt-tracker.yaml
  • web-accessibility-auditor.yaml

Proposed Solutions

Option 1: Include subrecipes in deeplink

Modify documentation/src/utils/recipes.ts to include sub_recipes field in the encoded config:

const configForGoose = {
  title: cleaned.title,
  description: cleaned.description,
  instructions: cleaned.instructions,
  prompt: cleaned.prompt,
  activities: cleaned.activities,
  extensions: cleaned.extensions,
  parameters: (cleaned as any).parameters || [],
  sub_recipes: cleaned.sub_recipes || []  // Add this
};

However, this would require also encoding the subrecipe file contents, which could make deeplinks very large.

Option 2: Fetch subrecipes separately (Recommended)

When Desktop decodes a recipe with subrecipes, fetch the subrecipe files from the GitHub repository. This is similar to how the CLI handles it with git archive.

Code References

  • Website encoding: documentation/src/utils/recipes.ts (line ~10-17)
  • Desktop decoding: crates/goose-server/src/routes/recipe.rs (decode_recipe function)
  • CLI (working): crates/goose-cli/src/recipes/github_recipe.rs (get_folder_from_github function)
  • Subrecipe resolution: crates/goose/src/recipe/build_recipe/mod.rs (resolve_sub_recipe_path function)

Environment

  • Goose version: main branch (as of 2025-11-05)
  • Platform: All platforms (Desktop app)
  • Recipe source: https://block.github.io/goose/recipes

angiejones avatar Nov 05 '25 01:11 angiejones

I don't see immediately how we can do something about this; deeplinks and subrecipes don't work well toghether since deeplinks contain the entire recipe and so they don't know where they came from and therefor don't know where the subrecipes are located they can refer to.

what we can do is support the github repository setting in the desktop. that would then allow you to see all the recipes from the repository in the desktop and would allow you to run because now we would actually point to the actual file and not the deeplink

a possible alternative would be to encode in the recipe the url basis of where it came from when turning them into a deeplink (like add a base_url parameter to it). that seems scary from a security perspective though since now if you start that recipe, it could be dragging anything from anywhere on the internet

DOsinga avatar Nov 05 '25 02:11 DOsinga

I was chatting with @DOsinga offline about this a bit and wanted to capture some of our notes here.

The essential shape of the problem we're solving is package management. We want to provide easy ways for people to install recipes and those recipes include sub-recipes that are not fully defined (prompt, tools, etc) within the scope of what we have locally (IE we need to fetch their definitions).

This obviously has a mix of challenges in both implementation and security but we also have prior art in this space

Goose Recipe Management

We could have recipes define sub-recipes as follows:

subrecipe:
- url: <url>
  sha: <sha>

Where url defines the location to download the raw recipe file and sha is the sha256 checksum of the recipe content we expect to recieve.

Security Model

The originating recipe itself attests to the expected content of the sub-recipe which means in the event the sub-recipe changes unexpectedly we can fail safely on behalf of our users.

This pattern also nests well as the SHA of a given recipe implicitly is calculated with downstream sub-recipes as inputs. This ensures we are getting the full "recipe dependency tree" that a given recipe expects (and encourages recipe versioning/version control).

Future Options

We could potentially extend this to distrubute "signed recipes" IE recipes that include known good prompts and components (as we have a way of ensuring the components don't change out from under us due to the checksums)

We could also extend this to look like other package managers and have a lock-file of sorts capturing the full recipe tree (or a full manifest file per recipe). The use-case for that isn't immediately apparent but having the SHAs in-place seems like a good starting point that we can build on-top of into the future.

shellz-n-stuff avatar Nov 07 '25 03:11 shellz-n-stuff