Feature: Consolidated Resource Combined scenarios from: resource_handler_service_coverage, resource_handlers, resource_registry_model, resource_type_model # ============================================================ # Originally from: resource_handler_service_coverage.feature # Feature: ResourceHandlerService full coverage # ============================================================ Scenario: Resolving a deferred binding raises ValueError Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs binding that is deferred with slot name "repo" When I resolve the rhs single binding Then an rhs ValueError is raised with message containing "Cannot resolve deferred binding for slot 'repo'" Scenario: Resolving a binding with no resource_id raises ValueError Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs binding with no resource_id and slot name "data" When I resolve the rhs single binding Then an rhs ValueError is raised with message containing "Cannot resolve deferred binding for slot 'data'" # --- resolve_binding: successful resolution --- Scenario: Resolving a valid binding returns a BoundResource Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "repo" and resource_id "RES001" And rhs resource "RES001" typed "git-checkout" located at "/tmp/repo" with strategy "none" And an rhs type spec "git-checkout" with no handler and sandbox strategy "git_worktree" And the rhs sandbox manager returns a sandbox with path "/sandbox/abc" When I resolve the rhs single binding with plan_id "plan-1" and access "read_write" Then the rhs result is a BoundResource with slot "repo" and sandbox_path "/sandbox/abc" And the rhs BoundResource has resource_id "RES001" and access "read_write" # --- _resolve_handler_for_type: type_spec has a handler --- Scenario: Handler is resolved from type_spec handler reference Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "code" and resource_id "RES002" And rhs resource "RES002" typed "custom/py" located at "/src" with strategy "none" And an rhs type spec "custom/py" with handler "some.module:Handler" and sandbox strategy "copy_on_write" And the rhs resolve_handler function returns a mock handler And the rhs mock handler resolves to a BoundResource with sandbox_path "/sandbox/custom" When I resolve the rhs single binding with plan_id "plan-2" and access "read_only" Then the rhs result is a BoundResource with slot "code" and sandbox_path "/sandbox/custom" # --- _resolve_handler_for_type: handler resolution fails, falls back --- Scenario: Handler resolution failure falls back to DefaultHandler Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "files" and resource_id "RES003" And rhs resource "RES003" typed "custom/broken" located at "/data" with strategy "copy_on_write" And an rhs type spec "custom/broken" with handler "bad.module:Missing" and sandbox strategy "copy_on_write" And the rhs resolve_handler function raises HandlerResolutionError And the rhs sandbox manager returns a sandbox with path "/sandbox/fallback" When I resolve the rhs single binding with plan_id "plan-3" and access "read_only" Then the rhs result is a BoundResource with slot "files" and sandbox_path "/sandbox/fallback" # --- _resolve_handler_for_type: no handler on type_spec --- Scenario: No handler on type_spec uses DefaultHandler directly Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "dir" and resource_id "RES004" And rhs resource "RES004" typed "fs-directory" located at "/home/user" with strategy "copy_on_write" And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write" And the rhs sandbox manager returns a sandbox with path "/sandbox/dir" When I resolve the rhs single binding with plan_id "plan-4" and access "read_only" Then the rhs result is a BoundResource with slot "dir" and sandbox_path "/sandbox/dir" # --- resolve_bindings: mixed deferred and non-deferred --- Scenario: resolve_bindings skips deferred bindings and resolves the rest Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs binding list with: | slot_name | resource_id | deferred | | repo | RES010 | false | | param | | true | | config | RES011 | false | And rhs resource "RES010" typed "git-checkout" located at "/repo" with strategy "none" And rhs resource "RES011" typed "fs-directory" located at "/config" with strategy "none" And an rhs type spec "git-checkout" with no handler and sandbox strategy "git_worktree" And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write" And the rhs sandbox manager returns a sandbox with path "/sandbox/multi" When I resolve all rhs bindings with plan_id "plan-5" and access "read_only" Then the rhs result dict has keys "repo" and "config" And the rhs result dict does not contain key "param" # --- resolve_bindings: failure propagation --- Scenario: resolve_bindings raises when a non-deferred binding fails Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs binding list with: | slot_name | resource_id | deferred | | broken | RES_MISSING | false | And the rhs resource lookup raises NotFoundError for "RES_MISSING" When I resolve all rhs bindings expecting an error Then an rhs NotFoundError is raised # --- resolve_bindings: all deferred returns empty dict --- Scenario: resolve_bindings with all deferred bindings returns empty dict Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs binding list with: | slot_name | resource_id | deferred | | a | | true | | b | | true | When I resolve all rhs bindings with plan_id "plan-6" and access "read_only" Then the rhs result dict is empty # --- resolve_resource: direct resolution --- Scenario: resolve_resource resolves a resource directly without a binding Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given rhs resource "RES020" typed "git-checkout" located at "/direct/repo" with strategy "none" And an rhs type spec "git-checkout" with no handler and sandbox strategy "git_worktree" And the rhs sandbox manager returns a sandbox with path "/sandbox/direct" When I resolve the rhs resource directly with plan_id "plan-7" slot "src" and access "read_write" Then the rhs result is a BoundResource with slot "src" and sandbox_path "/sandbox/direct" And the rhs BoundResource has resource_id "RES020" and access "read_write" # --- _DefaultHandler: resource sandbox_strategy overrides type --- Scenario: DefaultHandler uses resource sandbox_strategy when present Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "db" and resource_id "RES030" And rhs resource "RES030" typed "custom/db" located at "/data/db" with strategy "snapshot" And an rhs type spec "custom/db" with no handler and sandbox strategy "copy_on_write" And the rhs sandbox manager returns a sandbox with path "/sandbox/snap" When I resolve the rhs single binding with plan_id "plan-8" and access "read_only" Then the rhs sandbox manager was called with strategy "snapshot" # --- _DefaultHandler: falls back to type_spec sandbox_strategy --- Scenario: DefaultHandler falls back to type_spec sandbox_strategy when resource has none Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "fs" and resource_id "RES031" And rhs resource "RES031" typed "fs-directory" located at "/mnt/share" with strategy "none" And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write" And the rhs sandbox manager returns a sandbox with path "/sandbox/cow" When I resolve the rhs single binding with plan_id "plan-9" and access "read_only" Then the rhs sandbox manager was called with strategy "copy_on_write" # --- _DefaultHandler: no location error --- Scenario: DefaultHandler raises ValueError when resource has no location Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "empty" and resource_id "RES040" And rhs resource "RES040" typed "fs-directory" with no location And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write" When I resolve the rhs single binding Then an rhs ValueError is raised with message containing "has no location" # --- _DefaultHandler: sandbox context is None --- Scenario: DefaultHandler raises RuntimeError when sandbox has no context Given a mock sandbox manager for rhs And a mock resource lookup for rhs And a mock type lookup for rhs And a ResourceHandlerService under test Given an rhs non-deferred binding with slot "ctx" and resource_id "RES050" And rhs resource "RES050" typed "fs-directory" located at "/tmp/ctx" with strategy "none" And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write" And the rhs sandbox manager returns a sandbox with no context When I resolve the rhs single binding Then an rhs RuntimeError is raised with message containing "has no context" # ============================================================ # Originally from: resource_handlers.feature # Feature: Resource handler runtime # ============================================================ Scenario: GitCheckoutHandler satisfies ResourceHandler protocol Then GitCheckoutHandler should satisfy the ResourceHandler protocol Scenario: FsDirectoryHandler satisfies ResourceHandler protocol Then FsDirectoryHandler should satisfy the ResourceHandler protocol # === GitCheckoutHandler === Scenario: GitCheckoutHandler resolves a git-checkout resource Given a git-checkout resource with location "/tmp/test-repo" And a sandbox manager with a mock factory When I resolve the resource with GitCheckoutHandler for plan "PLAN001" Then the bound resource should have slot name "repo" And the bound resource should have resource type "git-checkout" And the bound resource sandbox path should not be empty Scenario: GitCheckoutHandler uses resource-level strategy override Given a git-checkout resource at "/tmp/test-repo" using strategy "copy_on_write" And a sandbox manager with a mock factory When I resolve the resource with GitCheckoutHandler for plan "PLAN002" Then the sandbox was created with strategy "copy_on_write" Scenario: GitCheckoutHandler rejects resource without location Given a git-checkout resource without location And a sandbox manager with a mock factory When I try to resolve the resource with GitCheckoutHandler Then a handler ValueError should be raised And the handler error should mention "no location" # === FsDirectoryHandler === Scenario: FsDirectoryHandler resolves an fs-directory resource Given an fs-directory resource with location "/tmp/test-dir" And a sandbox manager with a mock factory When I resolve the resource with FsDirectoryHandler for plan "PLAN003" Then the bound resource should have slot name "workdir" And the bound resource should have resource type "fs-directory" And the bound resource sandbox path should not be empty Scenario: FsDirectoryHandler uses copy_on_write by default Given an fs-directory resource with location "/tmp/test-dir" And a sandbox manager with a mock factory When I resolve the resource with FsDirectoryHandler for plan "PLAN004" Then the sandbox was created with strategy "copy_on_write" Scenario: FsDirectoryHandler rejects resource without location Given an fs-directory resource without location And a sandbox manager with a mock factory When I try to resolve the resource with FsDirectoryHandler Then a handler ValueError should be raised # === Handler Resolver === Scenario: Resolve handler from valid module:class string When I resolve handler "cleveragents.resource.handlers.git_checkout:GitCheckoutHandler" Then the resolved handler should be a GitCheckoutHandler instance Scenario: Resolve handler from fs-directory reference When I resolve handler "cleveragents.resource.handlers.fs_directory:FsDirectoryHandler" Then the resolved handler should be a FsDirectoryHandler instance Scenario: Resolve handler caches instances When I resolve handler "cleveragents.resource.handlers.git_checkout:GitCheckoutHandler" And I resolve the same handler reference again Then both resolved handlers should be the same object Scenario: Resolve handler rejects empty string When I try to resolve an empty handler reference Then a HandlerResolutionError should be raised And the resolution error should mention "empty" Scenario: Resolve handler rejects missing colon separator When I try to resolve handler "cleveragents.resource.handlers.git_checkout" Then a HandlerResolutionError should be raised And the resolution error should mention "module.path:ClassName" Scenario: Resolve handler rejects nonexistent module When I try to resolve handler "nonexistent.module:SomeClass" Then a HandlerResolutionError should be raised And the resolution error should mention "Cannot import" Scenario: Resolve handler rejects nonexistent class in valid module When I try to resolve handler "cleveragents.resource.handlers.git_checkout:NonExistentClass" Then a HandlerResolutionError should be raised And the resolution error should mention "not found" # === ResourceHandlerService === Scenario: ResourceHandlerService resolves a binding to BoundResource Given a resource handler service with mock lookups And a binding result for slot "repo" with resource id "RES001" When I resolve the binding via resource handler service for plan "PLAN010" Then the handler service should return a BoundResource And the handler service BoundResource slot should be "repo" And the handler service BoundResource sandbox path should not be empty Scenario: ResourceHandlerService skips deferred bindings Given a resource handler service with mock lookups And a deferred binding result for slot "extra" And a binding result for slot "repo" with resource id "RES001" When I resolve all bindings via resource handler service for plan "PLAN011" Then the handler service should return 1 bound resource And the handler service should have resolved slot "repo" Scenario: ResourceHandlerService rejects deferred single binding Given a resource handler service with mock lookups And a deferred binding result for slot "extra" When I try to resolve the single deferred binding via resource handler service Then a handler ValueError should be raised And the handler error should mention "deferred" Scenario: ResourceHandlerService uses fallback handler when type has no handler string Given a resource handler service with mock lookups and no handler string And a binding result for slot "data" with resource id "RES002" When I resolve the binding via resource handler service for plan "PLAN012" Then the handler service should return a BoundResource And the handler service BoundResource sandbox path should not be empty # ============================================================ # Originally from: resource_registry_model.feature # Feature: Resource Registry Domain Model # ============================================================ Scenario: PhysVirt has exactly two values Then the PhysVirt enum should have values "physical, virtual" Scenario: PhysVirt physical value Given a PhysVirt value of "physical" Then the PhysVirt string value should be "physical" Scenario: PhysVirt virtual value Given a PhysVirt value of "virtual" Then the PhysVirt string value should be "virtual" Scenario: PhysVirt rejects invalid value When I try to create a PhysVirt with value "hybrid" Then a resource validation error should be raised # SandboxStrategy enum Scenario: SandboxStrategy has exactly six values Then the SandboxStrategy enum should have values "git_worktree, copy_on_write, transaction_rollback, snapshot, overlay, none" Scenario Outline: SandboxStrategy accepts valid values Given a SandboxStrategy value of "" Then the SandboxStrategy string value should be "" Examples: | strategy | | git_worktree | | copy_on_write | | transaction_rollback | | snapshot | | overlay | | none | Scenario: SandboxStrategy accepts overlay Given a SandboxStrategy value of "overlay" Then the SandboxStrategy string value should be "overlay" Scenario: SandboxStrategy rejects versioning When I try to create a SandboxStrategy with value "versioning" Then a resource validation error should be raised # ResourceCapabilities model Scenario: ResourceCapabilities defaults Given default ResourceCapabilities Then the capability readable should be true And the capability writable should be true And the capability sandboxable should be true And the capability checkpointable should be true Scenario: ResourceCapabilities with custom values Given ResourceCapabilities with readable false and checkpointable true Then the capability readable should be false And the capability writable should be true And the capability sandboxable should be true And the capability checkpointable should be true Scenario: ResourceCapabilities is frozen Given default ResourceCapabilities When I try to mutate a ResourceCapabilities field Then a resource validation error should be raised # Resource model - creation Scenario: Create a minimal physical resource Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical" Then the resource resource_id should be "01HZQX7K8M3N4P5R6S7T8V9W0X" And the resource type name should be "git-checkout" And the resource classification should be "physical" And the resource name should be empty And the resource description should be empty And the resource properties should be empty And the resource parents should be empty And the resource children should be empty And the resource linked_projects should be empty And the resource created_at should be set And the resource updated_at should be set Scenario: Create a full resource with all fields Given a full Resource with id "01HZQX7K8M3N4P5R6S7T8V9W0X" name "local/my-repo" type "git-checkout" classification "physical" description "Main repo" Then the resource name should be "local/my-repo" And the resource description should be "Main repo" Scenario: Resource with sandbox strategy override Given a Resource with sandbox strategy "git_worktree" Then the resource sandbox strategy should be "git_worktree" Scenario: Resource with non-sandboxable rejects checkpointable When I try to create a Resource with sandboxable false and checkpointable true Then a resource validation error should be raised # Resource model - validation Scenario: Resource rejects invalid ULID When I try to create a Resource with resource_id "invalid-ulid" Then a resource validation error should be raised Scenario: Resource rejects empty resource_type_name When I try to create a Resource with empty resource_type_name Then a resource validation error should be raised Scenario: Resource rejects empty string name When I try to create a Resource with empty string name Then a resource validation error should be raised Scenario: Resource accepts None name Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical" Then the resource name should be empty # Resource model - domain properties Scenario: Physical resource is_physical Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical" Then the resource is_physical should be true And the resource is_virtual should be false Scenario: Virtual resource is_virtual Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git" and classification "virtual" Then the resource is_physical should be false And the resource is_virtual should be true Scenario: Read-only resource Given a Resource with writable false Then the resource is_read_only should be true Scenario: Writable resource Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical" Then the resource is_read_only should be false # Resource model - frozen Scenario: Resource is frozen Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical" When I try to mutate a Resource field Then a resource validation error should be raised # Resource model - with_updated_at Scenario: with_updated_at returns new instance Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical" When I call with_updated_at on the resource Then the new resource should have a different updated_at And the new resource should have the same resource_id # Resource model - content_hash and location Scenario: Resource with content_hash Given a Resource with content_hash "sha256:abc123" Then the resource content_hash should be "sha256:abc123" Scenario: Resource with location Given a Resource with location "/home/user/repos/main" Then the resource location should be "/home/user/repos/main" Scenario: Resource rejects empty location string When I try to create a Resource with empty location Then a resource validation error should be raised # Resource model - parents and children Scenario: Resource with parents and children Given a Resource with parents and children Then the resource should have 1 parent And the resource should have 2 children # ============================================================ # Originally from: resource_type_model.feature # Feature: Resource Type Model and Schema Loader # ============================================================ Scenario: Built-in resource type with unnamespaced name Given a resource type config with name "git-checkout" and built_in true When I create a ResourceTypeSpec from config Then the resource type should be valid And the rt_spec name should be "git-checkout" Scenario: Custom resource type with namespaced name Given a resource type config with name "myorg/custom-db" and built_in false When I create a ResourceTypeSpec from config Then the resource type should be valid And the rt_spec name should be "myorg/custom-db" Scenario: Custom type without namespace is rejected Given a resource type config with name "no-namespace" and built_in false When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "namespace/name" Scenario: Invalid name with special characters is rejected Given a resource type config with name "bad!name" and built_in false When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "Invalid resource type name" # ── ResourceKind ──────────────────────────────────────────── Scenario: Physical resource kind Given a resource type config with resource_kind "physical" When I create a ResourceTypeSpec from config Then the resource type resource_kind should be "physical" Scenario: Virtual resource kind Given a resource type config with resource_kind "virtual" and equivalence set When I create a ResourceTypeSpec from config Then the resource type resource_kind should be "virtual" Scenario: Virtual resource kind without equivalence is rejected Given a resource type config with resource_kind "virtual" and no equivalence When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "equivalence" # ── SandboxStrategy ───────────────────────────────────────── Scenario: Git worktree sandbox strategy Given a resource type config with sandbox_strategy "git_worktree" When I create a ResourceTypeSpec from config Then the resource type sandbox_strategy should be "git_worktree" Scenario: Copy on write sandbox strategy Given a resource type config with sandbox_strategy "copy_on_write" When I create a ResourceTypeSpec from config Then the resource type sandbox_strategy should be "copy_on_write" Scenario: Transaction rollback sandbox strategy Given a resource type config with sandbox_strategy "transaction_rollback" When I create a ResourceTypeSpec from config Then the resource type sandbox_strategy should be "transaction_rollback" Scenario: Snapshot sandbox strategy Given a resource type config with sandbox_strategy "snapshot" When I create a ResourceTypeSpec from config Then the resource type sandbox_strategy should be "snapshot" Scenario: None sandbox strategy Given a resource type config with sandbox_strategy "none" When I create a ResourceTypeSpec from config Then the resource type sandbox_strategy should be "none" # ── CLI Arguments ─────────────────────────────────────────── Scenario: Valid CLI argument parsing Given a resource type config with a cli_arg named "path" of type "path" When I create a ResourceTypeSpec from config Then the resource type should have 1 cli_arg And the first cli_arg name should be "path" Scenario: Multiple CLI arguments Given a resource type config with cli_args "path" and "branch" When I create a ResourceTypeSpec from config Then the resource type should have 2 cli_args Scenario: CLI argument with invalid name is rejected Given a resource type config with a cli_arg named "BadName" of type "string" When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "not CLI-safe" Scenario: CLI argument with invalid type is rejected Given a resource type config with a cli_arg named "count" of type "bigint" When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "Invalid argument type" # ── Parent/Child Types ────────────────────────────────────── Scenario: Resource type with parent types Given a resource type config with parent_types "git-checkout" When I create a ResourceTypeSpec from config Then the resource type should have parent_types containing "git-checkout" Scenario: Resource type with child types Given a resource type config with child_types "fs-directory" When I create a ResourceTypeSpec from config Then the resource type should have child_types containing "fs-directory" Scenario: Resource type with both parent and child types Given a resource type config with parent_types_set "git-checkout" and child_types_set "fs-directory" When I create a ResourceTypeSpec from config Then the resource type should have parent_types containing "git-checkout" And the resource type should have child_types containing "fs-directory" # ── from_config() ─────────────────────────────────────────── Scenario: from_config with missing name Given a resource type config dict without name When I try to call from_config Then from_config should fail with "must include 'name'" Scenario: from_config with missing resource_kind Given a resource type config dict without resource_kind When I try to call from_config Then from_config should fail with "must include 'resource_kind'" Scenario: from_config with missing sandbox_strategy Given a resource type config dict without sandbox_strategy When I try to call from_config Then from_config should fail with "must include 'sandbox_strategy'" Scenario: from_config with valid full config Given a full resource type config dict When I call from_config Then the resource type spec should be valid # ── as_cli_dict() ─────────────────────────────────────────── Scenario: as_cli_dict returns expected keys Given a resource type config with name "myorg/test-type" and built_in false When I create a ResourceTypeSpec and call as_cli_dict Then the rt_cli dict should contain key "name" with value "myorg/test-type" And the rt_cli dict should contain key "resource_kind" And the rt_cli dict should contain key "sandbox_strategy" And the rt_cli dict should contain key "capabilities" # ── Schema loader ─────────────────────────────────────────── Scenario: Schema loader from YAML string Given a valid resource type YAML string When I load the resource type via from_yaml Then the loaded schema should be valid Scenario: Schema loader from YAML file Given a valid resource type YAML file When I load the resource type via from_yaml_file Then the loaded schema should be valid Scenario: Schema loader with env var interpolation Given a resource type YAML with env var "${RT_TEST_HANDLER}" And the env var "RT_TEST_HANDLER" is set to "test.handler:Handler" When I load the resource type via from_yaml Then the handler should be "test.handler:Handler" Scenario: Schema loader with empty YAML When I try to load an empty YAML string Then the schema loader should fail with "empty" Scenario: Schema loader with None YAML When I try to load a None YAML string Then the schema loader should fail with "None" Scenario: Schema loader with non-mapping YAML When I try to load a YAML list Then the schema loader should fail with "mapping" Scenario: Schema loader with unsupported version Given a resource type YAML with schema_version "99" When I try to load the resource type via from_yaml Then the schema loader should fail with "Unsupported schema version" Scenario: Schema loader from non-existent file When I try to load from a non-existent file Then the schema loader should raise FileNotFoundError Scenario: Schema loader from directory instead of file When I try to load from a directory path Then the schema loader should fail with "not a file" # ── Capabilities ──────────────────────────────────────────── Scenario: Default capabilities Given a resource type config with default capabilities When I create a ResourceTypeSpec from config Then the capabilities should have read true And the capabilities should have write true And the capabilities should have sandbox true And the capabilities should have checkpoint false Scenario: Custom capabilities Given a resource type config with custom capabilities When I create a ResourceTypeSpec from config Then the capabilities should have read true And the capabilities should have write false And the capabilities should have sandbox true And the capabilities should have checkpoint true Scenario: Unknown capability key is rejected Given a resource type config with unknown capability "teleport" When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "Unknown capability" # ── CLI argument edge cases ────────────────────────────────── Scenario: cli_args entry that is not a mapping is rejected Given a resource type config with a non-mapping cli_arg When I try to create a ResourceTypeSpec from config Then the resource type creation should fail with "must be a mapping" # ── as_cli_dict coverage ────────────────────────────────────── Scenario: as_cli_dict includes all optional fields when present Given a full resource type config with handler and auto_discovery When I create a ResourceTypeSpec and call as_cli_dict Then the rt_cli dict should contain key "description" And the rt_cli dict should contain key "cli_args" And the rt_cli dict should contain key "child_types" And the rt_cli dict should contain key "handler" And the rt_cli dict should contain key "auto_discovery" And the rt_cli dict should contain key "capabilities" Scenario: as_cli_dict with equivalence field Given a resource type config with resource_kind "virtual" and equivalence set When I create a ResourceTypeSpec and call as_cli_dict Then the rt_cli dict should contain key "equivalence" Scenario: as_cli_dict with parent_types Given a resource type config with parent_types "git-checkout" When I create a ResourceTypeSpec and call as_cli_dict Then the rt_cli dict should contain key "parent_types" # ── Schema-level validation ─────────────────────────────────── Scenario: Schema rejects invalid cli_arg name Given a resource type YAML with invalid cli_arg name When I try to load the resource type via from_yaml Then the schema loader should fail with "not CLI-safe" Scenario: Schema rejects invalid cli_arg type Given a resource type YAML with invalid cli_arg type When I try to load the resource type via from_yaml Then the schema loader should fail with "Invalid argument type" Scenario: Schema rejects invalid resource type name Given a resource type YAML with invalid name When I try to load the resource type via from_yaml Then the schema loader should fail with "Invalid resource type name" Scenario: Schema rejects invalid resource_kind Given a resource type YAML with invalid resource_kind When I try to load the resource type via from_yaml Then the schema loader should fail with "Invalid resource_kind" Scenario: Schema rejects invalid sandbox_strategy Given a resource type YAML with invalid sandbox_strategy When I try to load the resource type via from_yaml Then the schema loader should fail with "Invalid sandbox_strategy" Scenario: Schema rejects custom type without namespace Given a resource type YAML with custom type no namespace When I try to load the resource type via from_yaml Then the schema loader should fail with "namespace/name" Scenario: Schema rejects virtual type without equivalence Given a resource type YAML with virtual kind no equivalence When I try to load the resource type via from_yaml Then the schema loader should fail with "equivalence" Scenario: Schema loader from_yaml_file with None path When I try to load from a None file path Then the schema loader should fail with "None" Scenario: Schema env var interpolation in list items Given a resource type YAML with env var in list And the env var "RT_TEST_PARENT" is set to "custom-parent" When I load the resource type via from_yaml Then the loaded schema should have parent type "custom-parent" Scenario: Schema accepts overlay sandbox strategy Given a resource type YAML with sandbox_strategy "overlay" When I load the resource type via from_yaml Then the loaded schema sandbox_strategy should be "overlay" Scenario: Invalid sandbox_strategy error message lists overlay as allowed Given a resource type YAML with invalid sandbox_strategy When I try to load the resource type via from_yaml Then the schema loader should fail with "overlay"