Feature: Actor registry and loader As a CleverAgents user I want to discover, load, and cache actor YAML configs from the filesystem So that actors are available for compilation and execution # ──────────────────────────────────────────────────────────── # Discovery from search roots # ──────────────────────────────────────────────────────────── Scenario: Discover actors from a single directory Given a temporary actors directory with files | filename | name | type | model | | reviewer.yaml | assistants/reviewer | llm | gpt-4 | | formatter.yaml | utilities/formatter | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then the loader should find 2 actors And the loader should contain actor "assistants/reviewer" And the loader should contain actor "utilities/formatter" Scenario: Discover actors from multiple search roots Given a temporary actors directory "root_a" with files | filename | name | type | model | | writer.yaml | assistants/writer | llm | gpt-4 | And a temporary actors directory "root_b" with files | filename | name | type | model | | analyst.yaml | data/analyst | llm | gpt-4 | When I create an actor loader with both directories as search roots And I run discovery Then the loader should find 2 actors And the loader should contain actor "assistants/writer" And the loader should contain actor "data/analyst" Scenario: Only YAML files are discovered Given a temporary actors directory with files | filename | name | type | model | | valid.yaml | assistants/valid | llm | gpt-4 | And a non-YAML file "readme.txt" in the directory And a non-YAML file "config.json" in the directory When I create an actor loader with that directory as search root And I run discovery Then the loader should find 1 actors Scenario: Nested YAML files are discovered recursively Given a temporary actors directory with nested structure | subdir | filename | name | type | model | | sub | nested.yaml | assistants/nested | llm | gpt-4 | | sub/deep | deep.yaml | assistants/deep | llm | gpt-4 | And a top-level actor file | filename | name | type | model | | top.yaml | assistants/top | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then the loader should find 3 actors # ──────────────────────────────────────────────────────────── # Duplicate detection # ──────────────────────────────────────────────────────────── Scenario: Duplicate actor names across search roots emit consolidated error Given a temporary actors directory "dup_a" with files | filename | name | type | model | | writer.yaml | assistants/writer | llm | gpt-4 | And a temporary actors directory "dup_b" with files | filename | name | type | model | | writer2.yaml | assistants/writer | llm | gpt-4 | When I create an actor loader with both directories as search roots And I run discovery expecting errors Then the loader should raise a validation error mentioning "assistants/writer" Scenario: Duplicate actor names within a single search root emit error Given a temporary actors directory with files | filename | name | type | model | | one.yaml | assistants/helper | llm | gpt-4 | | two.yaml | assistants/helper | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery expecting errors Then the loader should raise a validation error mentioning "assistants/helper" # ──────────────────────────────────────────────────────────── # Namespace handling # ──────────────────────────────────────────────────────────── Scenario: Default local/ namespace applied when namespace omitted Given a temporary actors directory with a nameless actor file | filename | raw_name | type | model | | bare.yaml | my-agent | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then the loader should contain actor "local/my-agent" Scenario: Explicit namespace preserved as-is Given a temporary actors directory with files | filename | name | type | model | | custom.yaml | myteam/my-actor | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then the loader should contain actor "myteam/my-actor" # ──────────────────────────────────────────────────────────── # Namespaced lookup # ──────────────────────────────────────────────────────────── Scenario: Get actor by namespaced name Given a temporary actors directory with files | filename | name | type | model | | helper.yaml | assistants/helper | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then I can retrieve actor "assistants/helper" by name And the retrieved actor type should be "llm" Scenario: Get nonexistent actor returns None Given a temporary actors directory with files | filename | name | type | model | | helper.yaml | assistants/helper | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then getting actor "assistants/missing" should return None Scenario: List actors filtered by namespace Given a temporary actors directory with files | filename | name | type | model | | rev.yaml | assistants/reviewer | llm | gpt-4 | | fmt.yaml | utilities/formatter | llm | gpt-4 | | ana.yaml | assistants/analyzer | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery Then listing loaded actors with namespace "assistants" should return 2 actors And listing loaded actors with namespace "utilities" should return 1 actors # ──────────────────────────────────────────────────────────── # Content hash caching # ──────────────────────────────────────────────────────────── Scenario: Unchanged files are not reloaded on re-discovery Given a temporary actors directory with files | filename | name | type | model | | cached.yaml | assistants/cached | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery And I run discovery again Then the actor "assistants/cached" should have been loaded only once Scenario: Modified files are reloaded on re-discovery Given a temporary actors directory with files | filename | name | type | model | | mutable.yaml | assistants/mutable | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery And I modify the actor file "mutable.yaml" to change the model to "gpt-3.5-turbo" And I run discovery again Then the actor "assistants/mutable" model should be "gpt-3.5-turbo" # ──────────────────────────────────────────────────────────── # Validation at load time # ──────────────────────────────────────────────────────────── Scenario: Invalid YAML file is rejected with clear error Given a temporary actors directory with an invalid YAML file "bad.yaml" When I create an actor loader with that directory as search root And I run discovery expecting errors Then the loader should raise a validation error mentioning "bad.yaml" Scenario: YAML file missing required fields is rejected Given a temporary actors directory with a schema-invalid file "incomplete.yaml" When I create an actor loader with that directory as search root And I run discovery expecting errors Then the loader should raise a validation error # ──────────────────────────────────────────────────────────── # Integration with example actors # ──────────────────────────────────────────────────────────── Scenario: Discover all example actors from examples/actors/ Given the project examples/actors directory exists When I create an actor loader with the examples/actors directory And I run discovery Then the loader should find at least 5 actors # ──────────────────────────────────────────────────────────── # Cache invalidation # ──────────────────────────────────────────────────────────── Scenario: Clear cache removes all loaded actors Given a temporary actors directory with files | filename | name | type | model | | test.yaml | assistants/test | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery And I clear the loader cache Then the loader should find 0 actors Scenario: Deleted actor file is removed on re-discovery Given a temporary actors directory with files | filename | name | type | model | | keep.yaml | assistants/keep | llm | gpt-4 | | remove.yaml | assistants/removable | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery And I delete the actor file "remove.yaml" And I run discovery again Then the loader should find 1 actors And the loader should contain actor "assistants/keep" And getting actor "assistants/removable" should return None # ──────────────────────────────────────────────────────────── # Tool Registry integration # ──────────────────────────────────────────────────────────── Scenario: Actor loader resolves tool references through tool registry Given a tool registry with tool "files/read_file" And a temporary actors directory with a tool-actor file | filename | name | type | tools | | tools.yaml | utilities/file_ops | tool | files/read_file | When I create an actor loader with that directory and the tool registry And I run discovery Then the loader should contain actor "utilities/file_ops" And the actor "utilities/file_ops" tools should include "files/read_file" Scenario: Actor with unresolvable tool reference logs warning Given an empty tool registry And a temporary actors directory with a tool-actor file | filename | name | type | tools | | tools.yaml | utilities/ops | tool | missing/nonexistent | When I create an actor loader with that directory and the tool registry And I run discovery Then the loader should contain actor "utilities/ops" And the loader should have a warning about unresolved tool "missing/nonexistent" # ──────────────────────────────────────────────────────────── # Concurrency and race conditions # ──────────────────────────────────────────────────────────── @unit @actor @concurrency Scenario: list_actors with namespace filter is thread-safe under concurrent clear/discover Given a temporary actors directory with files | filename | name | type | model | | rev.yaml | assistants/reviewer | llm | gpt-4 | | fmt.yaml | utilities/formatter | llm | gpt-4 | | ana.yaml | assistants/analyzer | llm | gpt-4 | When I create an actor loader with that directory as search root And I run discovery And 5 threads concurrently call list_actors with namespace "assistants" while another thread calls clear Then all concurrent list_actors calls should return consistent results # ──────────────────────────────────────────────────────────── # Edge cases for coverage # ──────────────────────────────────────────────────────────── Scenario: Non-existent search root is skipped gracefully Given a non-existent directory path as search root When I create an actor loader with that directory as search root And I run discovery Then the loader should find 0 actors Scenario: YAML file containing a list instead of a mapping is rejected Given a temporary actors directory with a list-content YAML file "list.yaml" When I create an actor loader with that directory as search root And I run discovery expecting errors Then the loader should raise a validation error mentioning "list.yaml" Scenario: LLM actor without tools does not produce warnings with tool registry Given a tool registry with tool "files/read_file" And a temporary actors directory with files | filename | name | type | model | | simple.yaml | assistants/simple | llm | gpt-4 | When I create an actor loader with that directory and the tool registry And I run discovery Then the loader should contain actor "assistants/simple" And the loader should have 0 warnings