Feature: Actor configuration uncovered lines coverage As a developer maintaining actor configuration parsing I want robust coverage for actor config edge cases So that failures and overrides are validated Background: Given an isolated actor config workspace # --- load_blob_from_file: lines 39-55 --- Scenario: Missing config file surfaces helpful error When I load the actor config blob from "missing.json" Then a ValueError should be raised containing "Config file not found" Scenario: JSON null config becomes empty mapping Given an actor config file "null.json" with content: """ null """ When I load the actor config blob from "null.json" Then the loaded actor config blob should equal {} Scenario: JSON list config raises validation error Given an actor config file "list.json" with content: """ [1, 2, 3] """ When I load the actor config blob from "list.json" Then a ValueError should be raised containing "Config must be a JSON or YAML object" Scenario: Valid JSON config is loaded correctly Given an actor config file "valid.json" with content: """ {"provider": "openai", "model": "gpt-4"} """ When I load the actor config blob from "valid.json" Then the loaded actor config blob should equal {"provider": "openai", "model": "gpt-4"} # --- _load_v2_yaml_content: lines 59-60 (yaml unavailable) --- Scenario: YAML dependency absence surfaces requirement Given PyYAML parsing is unavailable for actor config And an actor config file "needs_yaml.yaml" with content: """ provider: openai """ When I load the actor config blob from "needs_yaml.yaml" Then a ValueError should be raised containing "PyYAML is required for YAML actor configs" # --- _load_v2_yaml_content: lines 63-106 (YAML parsing paths) --- Scenario: Empty YAML returns empty object Given an actor config file "empty.yaml" with content: """ """ When I load the actor config blob from "empty.yaml" Then the loaded actor config blob should equal {} Scenario: Non-object YAML raises validation error Given an actor config file "list.yaml" with content: """ - provider: openai """ When I load the actor config blob from "list.yaml" Then a ValueError should be raised containing "Config must be a JSON or YAML object" Scenario: Plain YAML config is loaded via safe_load path Given an actor config file "plain.yaml" with content: """ provider: openai model: gpt-4 """ When I load the actor config blob from "plain.yaml" Then the loaded actor config blob should equal {"provider": "openai", "model": "gpt-4"} # --- _load_v2_yaml_content: lines 64-92 (templated YAML branch) --- Scenario: Templated YAML preserves double-brace placeholders in system_prompt Given an actor config file "templated.yaml" with content: """ provider: openai model: gpt-4 system_prompt: "Use {{ context.paper_details.topic }} to write." """ When I load the actor config blob from "templated.yaml" Then the loaded actor config value at "system_prompt" should equal "Use {{ context.paper_details.topic }} to write." Scenario: Templated YAML preserves block-tag placeholders in system_prompt Given an actor config file "block_template.yaml" with content: """ provider: openai model: gpt-4 system_prompt: "{% if context.brainstorming_summary %}summary{% endif %}" """ When I load the actor config blob from "block_template.yaml" Then the loaded actor config value at "system_prompt" should contain "summary" # --- _restore_template_syntax: lines 110-130 (dict and list recursion) --- Scenario: Restore template syntax recurses into nested dicts Given an actor config file "nested_template.yaml" with content: """ provider: openai model: gpt-4 outer: system_prompt: "Hello {{ context.paper_details.topic }}" """ When I load the actor config blob from "nested_template.yaml" Then the loaded actor config nested value at "outer.system_prompt" should equal "Hello {{ context.paper_details.topic }}" Scenario: Restore template syntax recurses into lists Given an actor config file "list_template.yaml" with content: """ provider: openai model: gpt-4 messages: - role: system system_prompt: "Topic is {{ context.paper_details.topic }}" - role: user content: "plain text" """ When I load the actor config blob from "list_template.yaml" Then the loaded actor config nested value at "messages.0.system_prompt" should equal "Topic is {{ context.paper_details.topic }}" # --- _interpolate_env_vars: lines 134-174 (env var substitution and type coercion) --- Scenario: Environment variable interpolation uses default boolean value Given an actor config file "env_bool.yaml" with content: """ provider: openai model: gpt-4 options: flag: "${MISSING_BOOL_VAR:true}" """ And the actor config environment variable "MISSING_BOOL_VAR" is unset When I load the actor config blob from "env_bool.yaml" Then the loaded actor config value at "options.flag" should equal True Scenario: Environment variable interpolation uses default false boolean value Given an actor config file "env_false.yaml" with content: """ provider: openai model: gpt-4 options: flag: "${MISSING_BOOL_FALSE:false}" """ And the actor config environment variable "MISSING_BOOL_FALSE" is unset When I load the actor config blob from "env_false.yaml" Then the loaded actor config value at "options.flag" should equal False Scenario: Environment variable interpolation uses default integer value Given an actor config file "env_int.yaml" with content: """ provider: openai model: gpt-4 options: count: "${MISSING_INT_VAR:42}" """ And the actor config environment variable "MISSING_INT_VAR" is unset When I load the actor config blob from "env_int.yaml" Then the loaded actor config value at "options.count" should equal 42 Scenario: Environment variable interpolation uses default string value Given an actor config file "env_str.yaml" with content: """ provider: openai model: gpt-4 options: greeting: "${MISSING_STR_VAR:hello}" """ And the actor config environment variable "MISSING_STR_VAR" is unset When I load the actor config blob from "env_str.yaml" Then the loaded actor config value at "options.greeting" should equal "hello" Scenario: Environment variable interpolation uses default float value Given an actor config file "env_float.yaml" with content: """ provider: openai model: gpt-4 options: ratio: "${MISSING_FLOAT_VAR:2.5}" """ And the actor config environment variable "MISSING_FLOAT_VAR" is unset When I load the actor config blob from "env_float.yaml" Then the loaded actor config value at "options.ratio" should equal 2.5 Scenario: Environment variable interpolation reads set env var Given an actor config file "env_set.yaml" with content: """ provider: openai model: gpt-4 options: api_key: "${TEST_ACTOR_API_KEY}" """ And the actor config environment variable "TEST_ACTOR_API_KEY" is set to "secret123" When I load the actor config blob from "env_set.yaml" Then the loaded actor config value at "options.api_key" should equal "secret123" Scenario: Missing required environment variable raises an error Given an actor config file "required_env.yaml" with content: """ provider: openai model: gpt-4 secret: "${REQUIRED_SECRET}" """ And the actor config environment variable "REQUIRED_SECRET" is unset When I load the actor config blob from "required_env.yaml" Then a ValueError should be raised containing "Environment variable 'REQUIRED_SECRET' is not set" Scenario: Environment variable coerces top-level string true to boolean Given an actor config file "env_top_bool.yaml" with content: """ provider: openai model: gpt-4 enabled: true """ When I load the actor config blob from "env_top_bool.yaml" Then the loaded actor config value at "enabled" should equal True Scenario: Environment variable coerces string integer to int Given an actor config file "env_top_int.yaml" with content: """ provider: openai model: gpt-4 retries: 3 """ When I load the actor config blob from "env_top_int.yaml" Then the loaded actor config value at "retries" should equal 3 Scenario: Interpolation coerces negative integer default Given an actor config file "env_neg_int.yaml" with content: """ provider: openai model: gpt-4 options: offset: "${MISSING_NEG_INT:-5}" """ And the actor config environment variable "MISSING_NEG_INT" is unset When I load the actor config blob from "env_neg_int.yaml" Then the loaded actor config value at "options.offset" should equal -5 Scenario: Interpolation recurses into nested dicts and lists Given an actor config file "env_nested.yaml" with content: """ provider: openai model: gpt-4 options: items: - "${MISSING_ITEM_VAR:default_item}" """ And the actor config environment variable "MISSING_ITEM_VAR" is unset When I load the actor config blob from "env_nested.yaml" Then the loaded actor config nested value at "options.items.0" should equal "default_item" # --- from_file: lines 189-190 --- Scenario: from_file applies overrides and unsafe flag Given an actor config file "valid_ff.json" with content: """ {"provider": "file-provider", "model": "file-model", "options": {"k": "v"}, "graph_descriptor": {"node": true}} """ When I parse the actor configuration from file "valid_ff.json" with overrides: """ {"name": "cli-name", "graph_descriptor": {"node": "override"}, "unsafe": true} """ Then the actor configuration should have provider "file-provider" and model "file-model" And the actor configuration name should be "cli-name" And the actor configuration graph descriptor should equal {"node": "override"} And the actor configuration options should equal {"k": "v"} And the actor configuration unsafe flag should be true # --- from_blob: lines 236 (default_options), 238 (v2_options), 250 (missing model) --- Scenario: from_blob rejects missing provider When I build an actor configuration from blob {"model": "only-model"} Then a ValueError should be raised containing "provider is required" Scenario: from_blob rejects missing model When I build an actor configuration from blob {"provider": "only-provider"} Then a ValueError should be raised containing "model is required" Scenario: from_blob merges default and v2 and override options When I build an actor configuration from structured blob with defaults and overrides: """ { "blob": { "provider": "cli-provider", "model": "cli-model", "options": {"user": "blob"}, "agents": { "writer": { "config": { "options": {"v2": "v2-option"} } } } }, "default_options": {"base": "default"}, "option_overrides": {"user": "override", "extra": "added"} } """ Then the actor configuration should have provider "cli-provider" and model "cli-model" And the actor configuration options should equal {"base": "default", "v2": "v2-option", "user": "override", "extra": "added"} Scenario: from_blob with None blob defaults to empty data When I build actor config from None blob with provider and model overrides Then the actor configuration should have provider "override-provider" and model "override-model" # --- _extract_v2_actor: lines 275-307 --- Scenario: v2 YAML actor config infers provider and model Given an actor config file "v2.yaml" with content: """ cleveragents: default_router: main_router agents: paper_writer: type: llm config: provider: openai model: gpt-4 unsafe: true options: temperature: 0.5 routes: main_router: type: stream operators: - type: map params: agent: paper_writer publications: - __output__ """ When I parse the actor configuration from file "v2.yaml" with overrides: """ {} """ Then the actor configuration should have provider "openai" and model "gpt-4" And the actor configuration unsafe flag should be true And the actor configuration graph descriptor should include key "routes" And the actor configuration graph descriptor should include key "agents" And the actor configuration graph descriptor should include key "cleveragents" And the actor configuration options should equal {"temperature": 0.5} Scenario: v2 extraction includes merges and templates keys when present Given an actor config file "v2_merges.yaml" with content: """ agents: writer: config: provider: openai model: gpt-4 merges: combined: type: join templates: default: system_prompt: "hello" """ When I parse the actor configuration from file "v2_merges.yaml" with overrides: """ {} """ Then the actor configuration should have provider "openai" and model "gpt-4" And the actor configuration graph descriptor should include key "merges" And the actor configuration graph descriptor should include key "templates" Scenario: v2 extraction handles agent entry without config block Given an actor config file "v2_no_config.yaml" with content: """ provider: fallback-provider model: fallback-model agents: first: type: llm """ When I parse the actor configuration from file "v2_no_config.yaml" with overrides: """ {} """ Then the actor configuration should have provider "fallback-provider" and model "fallback-model" # --- _extract_v2_options: lines 316-326 --- Scenario: v2 extraction skips non-dict agent entries Given an actor config file "invalid_v2.yaml" with content: """ provider: fallback-provider model: fallback-model agents: first: not-a-dict """ When I parse the actor configuration from file "invalid_v2.yaml" with overrides: """ {} """ Then the actor configuration should have provider "fallback-provider" and model "fallback-model" Scenario: v2 options extraction returns None for agents without options Given an actor config file "v2_no_opts.yaml" with content: """ agents: writer: config: provider: openai model: gpt-4 """ When I parse the actor configuration from file "v2_no_opts.yaml" with overrides: """ {} """ Then the actor configuration should have provider "openai" and model "gpt-4" And the actor configuration options should equal {} Scenario: from_blob reads provider_type and model_id aliases When I build an actor configuration from blob {"provider_type": "aliased-provider", "model_id": "aliased-model"} Then the actor configuration should have provider "aliased-provider" and model "aliased-model" Scenario: from_blob resolves graph from graph key alias When I build actor config from blob using graph key alias Then the actor configuration graph descriptor should equal {"step": "one"} Scenario: from_blob sets unsafe from blob data When I build an actor configuration from blob {"provider": "p", "model": "m", "unsafe": True} Then the actor configuration should have provider "p" and model "m" And the actor configuration unsafe flag should be true Scenario: from_blob with non-dict graph coerces to None When I build an actor configuration from blob {"provider": "p", "model": "m", "graph_descriptor": "not-a-dict"} Then the actor configuration should have provider "p" and model "m" And the actor configuration graph descriptor should be None