fix(resources): fix resource_kind field to use physical boolean per spec and add cloud resource types #10647
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: branch
|
||||
description: "Cross-repo branch identity linking branches with the same name and HEAD"
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: commit
|
||||
description: "Cross-repo commit identity linking commits with the same hash"
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-exec-env
|
||||
description: "Execution environment variables and configuration inside a container"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-image
|
||||
description: "A container image identified by tag and/or digest"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-mount
|
||||
description: "A bind mount or volume mount inside a container instance"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-network
|
||||
description: "A container network (bridge, host, overlay, macvlan, or none)"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-port
|
||||
description: "A port mapping between host and container (e.g. 8080:80/tcp)"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-runtime
|
||||
description: "A container runtime engine (Docker, Podman, containerd, etc.)"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: container-volume
|
||||
description: "A named container volume managed by the runtime engine"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: directory
|
||||
description: "Cross-layer directory identity linking equivalent physical directories"
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: executable
|
||||
description: "System executable (language runtime, compiler, LSP server binary). Discovered from container or host PATH."
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: file
|
||||
description: "Cross-layer file identity linking equivalent physical files"
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: fs-directory
|
||||
description: "A filesystem directory"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: copy_on_write
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: fs-file
|
||||
description: "A regular file on the local filesystem"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: copy_on_write
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: fs-hardlink
|
||||
description: "A hard link on the local filesystem — a file with link count > 1"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: copy_on_write
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: fs-mount
|
||||
description: "A physical mount point on the local system"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: copy_on_write
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: fs-symlink
|
||||
description: "A symbolic link on the local filesystem"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: copy_on_write
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-branch
|
||||
description: "A named branch ref (e.g., main, feature/auth)"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-checkout
|
||||
description: "A local git repository checkout"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: git_worktree
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-commit
|
||||
description: "A specific commit object"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-remote
|
||||
description: "A remote URL configured on a git repo"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-stash
|
||||
description: "A stash entry (e.g., stash@{0})"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-submodule
|
||||
description: "A submodule reference — a pointer to another git repository at a specific commit and path"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-tag
|
||||
description: "A tag ref — lightweight or annotated (e.g., v1.0.0)"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-tree-entry
|
||||
description: "A blob entry in a tree — a specific file's content at a specific path and mode"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git-tree
|
||||
description: "A tree object — a directory listing at a specific commit"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version: "1.0"
|
||||
name: git
|
||||
description: "A git repository — the object database, refs, and full history"
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: lsp-document
|
||||
description: "Text document tracked by an LSP server. Provides semantic access: types, diagnostics, completions, references, symbols."
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: lsp-server
|
||||
description: "LSP server definition and running process. Provides semantic services for one or more languages."
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: lsp-workspace
|
||||
description: "Workspace root tracked by an LSP server. Corresponds to a workspaceFolder in the LSP specification."
|
||||
resource_kind: physical
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: remote
|
||||
description: "Cross-repo remote identity. Links git-remote resources across different repos that point to the same URL."
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: submodule
|
||||
description: "Cross-repo submodule identity. Links git-submodule resources across different repos with the same submodule URL and path."
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: symlink
|
||||
description: "Cross-layer symlink identity. Links fs-symlink and git-tree-entry (mode 120000) resources with the same name and target."
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: tag
|
||||
description: "Cross-repo tag identity linking tags with the same name and target"
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
schema_version: "1.0"
|
||||
name: tree
|
||||
description: "Cross-repo/cross-commit tree identity linking trees with the same hash"
|
||||
resource_kind: virtual
|
||||
physical: false
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
|
||||
@@ -9,7 +9,7 @@ Feature: Cloud Infrastructure Resources
|
||||
Given a cloud resource type definition for "aws-account"
|
||||
Then the cloud type name should be "aws-account"
|
||||
And the cloud type sandbox_strategy should be "none"
|
||||
And the cloud type resource_kind should be "physical"
|
||||
And the cloud type physical should be True
|
||||
And the cloud type should be built_in
|
||||
|
||||
Scenario: AWS account resource type has correct CLI args
|
||||
@@ -58,7 +58,7 @@ Feature: Cloud Infrastructure Resources
|
||||
Given a cloud resource type definition for "gcp"
|
||||
Then the cloud type name should be "gcp"
|
||||
And the cloud type sandbox_strategy should be "none"
|
||||
And the cloud type resource_kind should be "physical"
|
||||
And the cloud type physical should be True
|
||||
And the cloud type should be built_in
|
||||
|
||||
Scenario: GCP resource type has correct CLI args
|
||||
@@ -94,7 +94,7 @@ Feature: Cloud Infrastructure Resources
|
||||
Given a cloud resource type definition for "azure"
|
||||
Then the cloud type name should be "azure"
|
||||
And the cloud type sandbox_strategy should be "none"
|
||||
And the cloud type resource_kind should be "physical"
|
||||
And the cloud type physical should be True
|
||||
And the cloud type should be built_in
|
||||
|
||||
Scenario: Azure resource type has correct CLI args
|
||||
|
||||
@@ -592,13 +592,13 @@ Feature: Consolidated Resource
|
||||
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"
|
||||
Then the resource type physical should be True
|
||||
|
||||
|
||||
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"
|
||||
Then the resource type physical should be False
|
||||
|
||||
|
||||
Scenario: Virtual resource kind without equivalence is rejected
|
||||
|
||||
@@ -146,9 +146,9 @@ Feature: Built-in Resource Type Configurations
|
||||
Scenario: Both built-ins are physical resources
|
||||
Given the built-in git-checkout YAML file
|
||||
When I load the built-in YAML via from_yaml_file
|
||||
Then the builtin schema resource_kind should be "physical"
|
||||
Then the builtin schema physical should be True
|
||||
|
||||
Scenario: fs-directory is physical
|
||||
Given the built-in fs-directory YAML file
|
||||
When I load the built-in YAML via from_yaml_file
|
||||
Then the builtin schema resource_kind should be "physical"
|
||||
Then the builtin schema physical should be True
|
||||
|
||||
@@ -12,7 +12,7 @@ Feature: Container infrastructure resource types
|
||||
When I load the YAML via from_yaml_file for container_infra
|
||||
Then the container_infra schema should be valid
|
||||
And the container_infra schema name should be "<type_name>"
|
||||
And the container_infra schema resource_kind should be "physical"
|
||||
And the container_infra schema physical should be True
|
||||
|
||||
Examples:
|
||||
| type_name |
|
||||
|
||||
@@ -10,7 +10,7 @@ Feature: Deferred Physical Resource Types
|
||||
When I load the YAML via from_config for phys_deferred
|
||||
Then the spec should be valid for phys_deferred
|
||||
And the spec name should be "git" for phys_deferred
|
||||
And the spec resource_kind should be "physical" for phys_deferred
|
||||
And the spec physical should be True for phys_deferred
|
||||
And the spec sandbox_strategy should be "none" for phys_deferred
|
||||
And the spec user_addable should be true for phys_deferred
|
||||
And the spec built_in should be true for phys_deferred
|
||||
@@ -20,7 +20,7 @@ Feature: Deferred Physical Resource Types
|
||||
When I load the YAML via from_config for phys_deferred
|
||||
Then the spec should be valid for phys_deferred
|
||||
And the spec name should be "git-remote" for phys_deferred
|
||||
And the spec resource_kind should be "physical" for phys_deferred
|
||||
And the spec physical should be True for phys_deferred
|
||||
And the spec user_addable should be false for phys_deferred
|
||||
|
||||
Scenario: git-branch YAML validates for phys_deferred
|
||||
@@ -72,7 +72,7 @@ Feature: Deferred Physical Resource Types
|
||||
When I load the YAML via from_config for phys_deferred
|
||||
Then the spec should be valid for phys_deferred
|
||||
And the spec name should be "fs-symlink" for phys_deferred
|
||||
And the spec resource_kind should be "physical" for phys_deferred
|
||||
And the spec physical should be True for phys_deferred
|
||||
And the spec user_addable should be false for phys_deferred
|
||||
|
||||
Scenario: fs-hardlink YAML validates for phys_deferred
|
||||
@@ -80,7 +80,7 @@ Feature: Deferred Physical Resource Types
|
||||
When I load the YAML via from_config for phys_deferred
|
||||
Then the spec should be valid for phys_deferred
|
||||
And the spec name should be "fs-hardlink" for phys_deferred
|
||||
And the spec resource_kind should be "physical" for phys_deferred
|
||||
And the spec physical should be True for phys_deferred
|
||||
And the spec user_addable should be false for phys_deferred
|
||||
|
||||
# ── Parent/child constraints for phys_deferred ─────────────
|
||||
|
||||
@@ -28,17 +28,17 @@ Feature: Deferred Virtual Resource Types
|
||||
Scenario: remote is a virtual resource type for virt_deferred
|
||||
Given the built-in remote YAML file for virt_deferred
|
||||
When I load the YAML via schema for virt_deferred
|
||||
Then the virt_deferred schema resource_kind should be "virtual"
|
||||
Then the virt_deferred schema physical should be False
|
||||
|
||||
Scenario: submodule is a virtual resource type for virt_deferred
|
||||
Given the built-in submodule YAML file for virt_deferred
|
||||
When I load the YAML via schema for virt_deferred
|
||||
Then the virt_deferred schema resource_kind should be "virtual"
|
||||
Then the virt_deferred schema physical should be False
|
||||
|
||||
Scenario: symlink is a virtual resource type for virt_deferred
|
||||
Given the built-in symlink YAML file for virt_deferred
|
||||
When I load the YAML via schema for virt_deferred
|
||||
Then the virt_deferred schema resource_kind should be "virtual"
|
||||
Then the virt_deferred schema physical should be False
|
||||
|
||||
# ── Not user-addable ──────────────────────────────────────
|
||||
|
||||
@@ -149,21 +149,21 @@ Feature: Deferred Virtual Resource Types
|
||||
When I load the YAML as a ResourceTypeSpec for virt_deferred
|
||||
Then the virt_deferred domain model should be valid
|
||||
And the virt_deferred domain model name should be "remote"
|
||||
And the virt_deferred domain model resource_kind should be "virtual"
|
||||
And the virt_deferred domain model physical should be False
|
||||
|
||||
Scenario: submodule YAML loads as domain model for virt_deferred
|
||||
Given the built-in submodule YAML file for virt_deferred
|
||||
When I load the YAML as a ResourceTypeSpec for virt_deferred
|
||||
Then the virt_deferred domain model should be valid
|
||||
And the virt_deferred domain model name should be "submodule"
|
||||
And the virt_deferred domain model resource_kind should be "virtual"
|
||||
And the virt_deferred domain model physical should be False
|
||||
|
||||
Scenario: symlink YAML loads as domain model for virt_deferred
|
||||
Given the built-in symlink YAML file for virt_deferred
|
||||
When I load the YAML as a ResourceTypeSpec for virt_deferred
|
||||
Then the virt_deferred domain model should be valid
|
||||
And the virt_deferred domain model name should be "symlink"
|
||||
And the virt_deferred domain model resource_kind should be "virtual"
|
||||
And the virt_deferred domain model physical should be False
|
||||
|
||||
# ── Child types ───────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Feature: Filesystem Resource Type Configurations
|
||||
Scenario: fs-mount has correct resource kind
|
||||
Given the built-in fs-mount YAML file
|
||||
When I load the built-in YAML via from_yaml_file
|
||||
Then the builtin schema resource_kind should be "physical"
|
||||
Then the builtin schema physical should be True
|
||||
|
||||
Scenario: fs-mount has correct sandbox strategy
|
||||
Given the built-in fs-mount YAML file
|
||||
@@ -123,7 +123,7 @@ Feature: Filesystem Resource Type Configurations
|
||||
Scenario: fs-file has correct resource kind
|
||||
Given the built-in fs-file YAML file
|
||||
When I load the built-in YAML via from_yaml_file
|
||||
Then the builtin schema resource_kind should be "physical"
|
||||
Then the builtin schema physical should be True
|
||||
|
||||
Scenario: fs-file has copy_on_write sandbox
|
||||
Given the built-in fs-file YAML file
|
||||
|
||||
@@ -11,7 +11,7 @@ Feature: LSP Resource Types
|
||||
When I load the YAML via schema for lsp_rt
|
||||
Then the lsp_rt schema should be valid
|
||||
And the lsp_rt schema name should be "<type_name>"
|
||||
And the lsp_rt schema resource_kind should be "physical"
|
||||
And the lsp_rt schema physical should be True
|
||||
And the lsp_rt schema sandbox_strategy should be "none"
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
Feature: Resource Type Physical Boolean Field
|
||||
As a CleverAgents developer
|
||||
I want resource types to use a physical boolean field per spec
|
||||
So that the schema matches the specification (UAT #5124)
|
||||
|
||||
# ── Physical boolean field ──────────────────────────────────────────────
|
||||
|
||||
Scenario: ResourceTypeSpec uses physical boolean field
|
||||
Given a resource type config with physical True
|
||||
When I create a ResourceTypeSpec from config
|
||||
Then the spec physical field should be True
|
||||
And the spec should be a physical type
|
||||
|
||||
Scenario: ResourceTypeSpec virtual type uses physical False
|
||||
Given a resource type config with physical False
|
||||
When I create a ResourceTypeSpec from config
|
||||
Then the spec physical field should be False
|
||||
And the spec should be a virtual type
|
||||
|
||||
Scenario: ResourceTypeSpec from_config accepts physical boolean
|
||||
Given a resource type config dict with physical True
|
||||
When I call from_config on ResourceTypeSpec
|
||||
Then the resulting spec physical should be True
|
||||
|
||||
Scenario: ResourceTypeSpec from_config accepts legacy resource_kind physical
|
||||
Given a resource type config dict with resource_kind physical
|
||||
When I call from_config on ResourceTypeSpec
|
||||
Then the resulting spec physical should be True
|
||||
|
||||
Scenario: ResourceTypeSpec from_config accepts legacy resource_kind virtual
|
||||
Given a resource type config dict with resource_kind virtual
|
||||
When I call from_config on ResourceTypeSpec
|
||||
Then the resulting spec physical should be False
|
||||
|
||||
Scenario: ResourceTypeSpec as_cli_dict outputs physical field
|
||||
Given a physical resource type spec
|
||||
When I call as_cli_dict on the spec
|
||||
Then the cli dict should contain physical True
|
||||
And the cli dict should not contain resource_kind
|
||||
|
||||
Scenario: ResourceTypeConfigSchema uses physical boolean field
|
||||
Given a resource type YAML with physical true
|
||||
When I parse the YAML with ResourceTypeConfigSchema
|
||||
Then the schema physical field should be True
|
||||
|
||||
Scenario: ResourceTypeConfigSchema rejects missing physical field
|
||||
Given a resource type YAML without physical field
|
||||
When I parse the YAML with ResourceTypeConfigSchema
|
||||
Then a validation error should be raised
|
||||
|
||||
# ── AWS RDS resource type ────────────────────────────────────────────────
|
||||
|
||||
Scenario: aws-rds-instance type exists in registry
|
||||
Given the physical boolean resource type registry
|
||||
When I look up resource type "aws-rds-instance"
|
||||
Then the rds type should exist
|
||||
And the rds type physical should be True
|
||||
And the rds type should inherit from "cloud-database"
|
||||
|
||||
Scenario: aws-rds-instance has required CLI args
|
||||
Given the physical boolean resource type registry
|
||||
When I look up resource type "aws-rds-instance"
|
||||
Then the rds type should have cli arg "engine"
|
||||
And the rds type should have cli arg "instance-class"
|
||||
And the rds type cli arg "engine" should be required
|
||||
And the rds type cli arg "instance-class" should be required
|
||||
|
||||
Scenario: aws-rds-instance has optional CLI args
|
||||
Given the physical boolean resource type registry
|
||||
When I look up resource type "aws-rds-instance"
|
||||
Then the rds type should have cli arg "db-name"
|
||||
And the rds type should have cli arg "port"
|
||||
And the rds type cli arg "db-name" should not be required
|
||||
And the rds type cli arg "port" should not be required
|
||||
|
||||
Scenario: cloud-database base type exists
|
||||
Given the physical boolean resource type registry
|
||||
When I look up resource type "cloud-database"
|
||||
Then the cloud-database type should exist
|
||||
And the cloud-database type physical should be True
|
||||
|
||||
# ── AWS S3 resource type ─────────────────────────────────────────────────
|
||||
|
||||
Scenario: aws-s3-bucket type has S3-specific CLI args
|
||||
Given the physical boolean resource type registry
|
||||
When I look up resource type "aws-s3-bucket"
|
||||
Then the s3 type should have cli arg "bucket-name"
|
||||
And the s3 type should have cli arg "versioning"
|
||||
And the s3 type cli arg "bucket-name" should be required
|
||||
|
||||
# ── All built-in types use physical boolean ──────────────────────────────
|
||||
|
||||
Scenario: All built-in physical types have physical True
|
||||
Given the physical boolean resource type registry
|
||||
When I check all physical boolean built-in types
|
||||
Then all physical types should have physical True
|
||||
And no type should have a resource_kind field
|
||||
|
||||
Scenario: All built-in virtual types have physical False
|
||||
Given the physical boolean resource type registry
|
||||
When I check all physical boolean built-in types
|
||||
Then all virtual types should have physical False
|
||||
@@ -25,7 +25,7 @@ Feature: Virtual Core Resource Types
|
||||
Scenario Outline: Virtual core types have resource_kind virtual for vcore
|
||||
Given the built-in <type_name> virtual YAML file for vcore
|
||||
When I load the virtual YAML via from_yaml_file for vcore
|
||||
Then the vcore schema resource_kind should be "virtual"
|
||||
Then the vcore schema physical should be False
|
||||
|
||||
Examples:
|
||||
| type_name |
|
||||
@@ -260,7 +260,7 @@ Feature: Virtual Core Resource Types
|
||||
Given the bootstrap builtin type definitions for vcore
|
||||
When I find the virtual type "<type_name>" in builtin definitions for vcore
|
||||
Then the found builtin type user_addable should be false for vcore
|
||||
And the found builtin type resource_kind should be "virtual" for vcore
|
||||
And the found builtin type physical should be False for vcore
|
||||
And the found builtin type should have equivalence for vcore
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -322,7 +322,7 @@ def step_cloud_type_strategy(context: Any, strategy: str) -> None:
|
||||
assert context.cloud_type_def["sandbox_strategy"] == strategy # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@then('the cloud type resource_kind should be "{kind}"')
|
||||
@then("the cloud type physical should be {value}")
|
||||
def step_cloud_type_kind(context: Any, kind: str) -> None:
|
||||
"""Check cloud type resource kind."""
|
||||
assert context.cloud_type_def["resource_kind"] == kind # type: ignore[attr-defined]
|
||||
|
||||
@@ -60,7 +60,7 @@ def step_yaml_nonexistent_parent(context: Context) -> None:
|
||||
{
|
||||
"name": "acme/child",
|
||||
"description": "Child type",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"inherits": "nonexistent-parent",
|
||||
"user_addable": True,
|
||||
@@ -80,7 +80,7 @@ def step_yaml_chain_error(context: Context) -> None:
|
||||
{
|
||||
"name": "acme/chain-child",
|
||||
"description": "Chain child",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"inherits": "git-checkout",
|
||||
}
|
||||
@@ -94,7 +94,7 @@ def step_yaml_duplicate(context: Context) -> None:
|
||||
{
|
||||
"name": "git-checkout",
|
||||
"description": "Duplicate",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -165,7 +165,7 @@ def step_register_custom_type(context: Context, name: str) -> None:
|
||||
{
|
||||
"name": name,
|
||||
"description": f"Custom type {name}",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": True,
|
||||
}
|
||||
@@ -184,7 +184,7 @@ def step_register_child_type(context: Context, child: str, parent: str) -> None:
|
||||
{
|
||||
"name": child,
|
||||
"description": f"Child type {child}",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"inherits": parent,
|
||||
}
|
||||
|
||||
@@ -62,9 +62,11 @@ def step_builtin_schema_sandbox(context: object, strategy: str) -> None:
|
||||
assert context.builtin_schema.sandbox_strategy == strategy # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@then('the builtin schema resource_kind should be "{kind}"')
|
||||
@then("the builtin schema physical should be {value}")
|
||||
def step_builtin_schema_kind(context: object, kind: str) -> None:
|
||||
assert context.builtin_schema.resource_kind == kind # type: ignore[attr-defined]
|
||||
# kind is "True" or "False" string from feature file
|
||||
expected = kind.lower() == "true"
|
||||
assert context.builtin_schema.physical is expected # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@then('the builtin schema parent_types should contain "{parent}"')
|
||||
|
||||
@@ -19,10 +19,7 @@ from cleveragents.application.services._resource_registry_container import (
|
||||
from cleveragents.application.services._resource_registry_data import (
|
||||
BUILTIN_TYPES,
|
||||
)
|
||||
from cleveragents.domain.models.core.resource_type import (
|
||||
ResourceKind,
|
||||
ResourceTypeSpec,
|
||||
)
|
||||
from cleveragents.domain.models.core.resource_type import ResourceTypeSpec
|
||||
from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
|
||||
_EXAMPLES_DIR = (
|
||||
@@ -78,7 +75,7 @@ def step_schema_name(context: object, name: str) -> None:
|
||||
assert context.ci_schema.name == name
|
||||
|
||||
|
||||
@then('the container_infra schema resource_kind should be "{kind}"')
|
||||
@then("the container_infra schema physical should be {value}")
|
||||
def step_schema_kind(context: object, kind: str) -> None:
|
||||
assert context.ci_schema.resource_kind == kind
|
||||
|
||||
@@ -100,7 +97,7 @@ def step_domain_name(context: object, name: str) -> None:
|
||||
|
||||
@then("the container_infra domain model resource_kind should be physical")
|
||||
def step_domain_kind_physical(context: object) -> None:
|
||||
assert context.ci_spec.resource_kind == ResourceKind.PHYSICAL
|
||||
assert context.ci_spec.physical is True
|
||||
|
||||
|
||||
# ── Then steps: parent/child ─────────────────────────────
|
||||
|
||||
@@ -72,7 +72,7 @@ def step_spec_name_for_phys_deferred(context: object, name: str) -> None:
|
||||
assert context.phys_deferred_spec.name == name # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@then('the spec resource_kind should be "{kind}" for phys_deferred')
|
||||
@then("the spec physical should be {value} for phys_deferred")
|
||||
def step_spec_kind_for_phys_deferred(context: object, kind: str) -> None:
|
||||
actual = context.phys_deferred_spec.resource_kind # type: ignore[attr-defined]
|
||||
assert str(actual) == kind or actual.value == kind, (
|
||||
@@ -186,7 +186,7 @@ def _base_physical_config(**overrides: Any) -> dict[str, Any]:
|
||||
"""Create a minimal physical type config."""
|
||||
config: dict[str, Any] = {
|
||||
"name": "testns/phys-test",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": False,
|
||||
|
||||
@@ -98,7 +98,7 @@ def step_schema_name_virt_deferred(context: object, name: str) -> None:
|
||||
assert actual == name, f"Expected schema name '{name}', got '{actual}'"
|
||||
|
||||
|
||||
@then('the virt_deferred schema resource_kind should be "{kind}"')
|
||||
@then("the virt_deferred schema physical should be {value}")
|
||||
def step_schema_kind_virt_deferred(context: object, kind: str) -> None:
|
||||
"""Assert the resource kind matches."""
|
||||
actual = context.virt_deferred_schema.resource_kind # type: ignore[attr-defined]
|
||||
@@ -212,7 +212,7 @@ def step_domain_name_virt_deferred(context: object, name: str) -> None:
|
||||
assert actual == name, f"Expected domain name '{name}', got '{actual}'"
|
||||
|
||||
|
||||
@then('the virt_deferred domain model resource_kind should be "{kind}"')
|
||||
@then("the virt_deferred domain model physical should be {value}")
|
||||
def step_domain_kind_virt_deferred(context: object, kind: str) -> None:
|
||||
"""Assert the domain model resource kind matches."""
|
||||
actual = context.virt_deferred_domain.resource_kind # type: ignore[attr-defined]
|
||||
@@ -411,7 +411,7 @@ def step_create_virtual_no_equiv_virt_deferred(context: object) -> None:
|
||||
ResourceTypeSpec.from_config(
|
||||
{
|
||||
"name": "test-virtual-no-equiv",
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
}
|
||||
@@ -427,7 +427,7 @@ def step_create_no_name_virt_deferred(context: object) -> None:
|
||||
try:
|
||||
ResourceTypeSpec.from_config(
|
||||
{
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
}
|
||||
)
|
||||
@@ -513,7 +513,7 @@ def step_create_criteria_non_string_virt_deferred(context: object) -> None:
|
||||
ResourceTypeSpec.from_config(
|
||||
{
|
||||
"name": "test-bad-criteria-int",
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
"equivalence": {"criteria": [123], "description": "bad"},
|
||||
@@ -533,7 +533,7 @@ def step_create_criteria_empty_string_virt_deferred(context: object) -> None:
|
||||
ResourceTypeSpec.from_config(
|
||||
{
|
||||
"name": "test-bad-criteria-empty",
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
"equivalence": {"criteria": [""], "description": "bad"},
|
||||
|
||||
@@ -92,7 +92,7 @@ def step_schema_name_lsp_rt(context: object, name: str) -> None:
|
||||
assert actual == name, f"Expected name '{name}', got '{actual}'"
|
||||
|
||||
|
||||
@then('the lsp_rt schema resource_kind should be "{kind}"')
|
||||
@then("the lsp_rt schema physical should be {value}")
|
||||
def step_schema_kind_lsp_rt(context: object, kind: str) -> None:
|
||||
"""Assert the resource kind matches."""
|
||||
actual = context.lsp_rt_schema.resource_kind # type: ignore[attr-defined]
|
||||
|
||||
@@ -6,7 +6,6 @@ import tempfile
|
||||
from behave import given, then, when
|
||||
|
||||
from cleveragents.domain.models.core.resource_type import (
|
||||
ResourceKind,
|
||||
ResourceTypeSpec,
|
||||
SandboxStrategy,
|
||||
)
|
||||
@@ -122,7 +121,7 @@ def step_config_with_parent_and_child(context, parent, child):
|
||||
@given("a resource type config dict without name")
|
||||
def step_config_without_name(context):
|
||||
context.rt_config = {
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "git_worktree",
|
||||
}
|
||||
|
||||
@@ -134,7 +133,7 @@ def step_config_without_kind(context):
|
||||
|
||||
@given("a resource type config dict without sandbox_strategy")
|
||||
def step_config_without_strategy(context):
|
||||
context.rt_config = {"name": "myorg/test", "resource_kind": "physical"}
|
||||
context.rt_config = {"name": "myorg/test", "physical": True}
|
||||
|
||||
|
||||
@given("a full resource type config dict")
|
||||
@@ -480,9 +479,12 @@ def step_rt_creation_fail(context, fragment):
|
||||
)
|
||||
|
||||
|
||||
@then('the resource type resource_kind should be "{kind}"')
|
||||
def step_rt_kind(context, kind):
|
||||
assert context.rt_spec.resource_kind == ResourceKind(kind)
|
||||
@then("the resource type physical should be {value}")
|
||||
def step_rt_kind(context, value):
|
||||
expected = value.lower() == "true"
|
||||
assert context.rt_spec.physical is expected, (
|
||||
f"Expected physical={expected}, got {context.rt_spec.physical}"
|
||||
)
|
||||
|
||||
|
||||
@then('the resource type sandbox_strategy should be "{strategy}"')
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
"""Step definitions for resource_type_physical_boolean.feature.
|
||||
|
||||
Tests for the physical boolean field replacing resource_kind enum,
|
||||
and for the new AWS RDS and S3 resource types.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from behave import given, then, when
|
||||
from pydantic import ValidationError
|
||||
|
||||
from cleveragents.application.services._resource_registry_cloud import (
|
||||
AWS_TYPES,
|
||||
CLOUD_BASE_TYPES,
|
||||
)
|
||||
from cleveragents.application.services._resource_registry_data import (
|
||||
BUILTIN_TYPES,
|
||||
)
|
||||
from cleveragents.domain.models.core.resource_type import ResourceTypeSpec
|
||||
from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Given steps
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("a resource type config with physical True")
|
||||
def step_config_physical_true(context: Any) -> None:
|
||||
"""Set up a physical resource type config."""
|
||||
context.config = {
|
||||
"name": "test-physical",
|
||||
"description": "A physical test type",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"built_in": True,
|
||||
}
|
||||
|
||||
|
||||
@given("a resource type config with physical False")
|
||||
def step_config_physical_false(context: Any) -> None:
|
||||
"""Set up a virtual resource type config."""
|
||||
context.config = {
|
||||
"name": "test-virtual",
|
||||
"description": "A virtual test type",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
"equivalence": {"criteria": ["content_hash"]},
|
||||
}
|
||||
|
||||
|
||||
@given("a resource type config dict with physical True")
|
||||
def step_config_dict_physical_true(context: Any) -> None:
|
||||
"""Set up a config dict with physical boolean."""
|
||||
context.config = {
|
||||
"name": "test-type",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
}
|
||||
|
||||
|
||||
@given("a resource type config dict with resource_kind physical")
|
||||
def step_config_dict_rk_physical(context: Any) -> None:
|
||||
"""Set up a legacy config dict with resource_kind string."""
|
||||
context.config = {
|
||||
"name": "test-type",
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
}
|
||||
|
||||
|
||||
@given("a resource type config dict with resource_kind virtual")
|
||||
def step_config_dict_rk_virtual(context: Any) -> None:
|
||||
"""Set up a legacy config dict with resource_kind virtual."""
|
||||
context.config = {
|
||||
"name": "test-virtual",
|
||||
"resource_kind": "virtual",
|
||||
"sandbox_strategy": "none",
|
||||
"built_in": True,
|
||||
"equivalence": {"criteria": ["content_hash"]},
|
||||
}
|
||||
|
||||
|
||||
@given("a physical resource type spec")
|
||||
def step_physical_spec(context: Any) -> None:
|
||||
"""Create a physical ResourceTypeSpec."""
|
||||
context.spec = ResourceTypeSpec(
|
||||
name="test-physical",
|
||||
physical=True,
|
||||
sandbox_strategy="copy_on_write",
|
||||
built_in=True,
|
||||
)
|
||||
|
||||
|
||||
@given("a resource type YAML with physical true")
|
||||
def step_yaml_physical_true(context: Any) -> None:
|
||||
"""Set up YAML with physical: true."""
|
||||
context.yaml_string = """
|
||||
name: myorg/test-type
|
||||
description: A test resource type
|
||||
physical: true
|
||||
sandbox_strategy: none
|
||||
"""
|
||||
|
||||
|
||||
@given("a resource type YAML without physical field")
|
||||
def step_yaml_no_physical(context: Any) -> None:
|
||||
"""Set up YAML without physical field."""
|
||||
context.yaml_string = """
|
||||
name: myorg/test-type
|
||||
description: A test resource type
|
||||
sandbox_strategy: none
|
||||
"""
|
||||
|
||||
|
||||
@given("the physical boolean resource type registry")
|
||||
def step_builtin_registry(context: Any) -> None:
|
||||
"""Load the built-in resource type registry."""
|
||||
context.registry = {t["name"]: t for t in BUILTIN_TYPES}
|
||||
context.cloud_base = {t["name"]: t for t in CLOUD_BASE_TYPES}
|
||||
context.aws_types = {t["name"]: t for t in AWS_TYPES}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# When steps
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@when("I create a ResourceTypeSpec from config")
|
||||
def step_create_spec_from_config(context: Any) -> None:
|
||||
"""Create a ResourceTypeSpec from the config."""
|
||||
context.spec = ResourceTypeSpec.from_config(context.config)
|
||||
|
||||
|
||||
@when("I call from_config on ResourceTypeSpec")
|
||||
def step_call_from_config(context: Any) -> None:
|
||||
"""Call ResourceTypeSpec.from_config."""
|
||||
context.spec = ResourceTypeSpec.from_config(context.config)
|
||||
|
||||
|
||||
@when("I call as_cli_dict on the spec")
|
||||
def step_call_as_cli_dict(context: Any) -> None:
|
||||
"""Call as_cli_dict on the spec."""
|
||||
context.cli_dict = context.spec.as_cli_dict()
|
||||
|
||||
|
||||
@when("I parse the YAML with ResourceTypeConfigSchema")
|
||||
def step_parse_yaml(context: Any) -> None:
|
||||
"""Parse YAML with ResourceTypeConfigSchema."""
|
||||
try:
|
||||
context.schema = ResourceTypeConfigSchema.from_yaml(context.yaml_string)
|
||||
context.validation_error = None
|
||||
except (ValidationError, ValueError) as e:
|
||||
context.schema = None
|
||||
context.validation_error = e
|
||||
|
||||
|
||||
@when('I look up resource type "{type_name}"')
|
||||
def step_lookup_type(context: Any, type_name: str) -> None:
|
||||
"""Look up a resource type in the registry."""
|
||||
context.type_name = type_name
|
||||
context.type_def = context.registry.get(type_name)
|
||||
|
||||
|
||||
@when("I check all physical boolean built-in types")
|
||||
def step_check_all_types(context: Any) -> None:
|
||||
"""Check all built-in types."""
|
||||
context.all_types = BUILTIN_TYPES
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Then steps
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@then("the spec physical field should be True")
|
||||
def step_spec_physical_true(context: Any) -> None:
|
||||
"""Assert spec.physical is True."""
|
||||
assert context.spec.physical is True, (
|
||||
f"Expected spec.physical to be True, got {context.spec.physical}"
|
||||
)
|
||||
|
||||
|
||||
@then("the spec physical field should be False")
|
||||
def step_spec_physical_false(context: Any) -> None:
|
||||
"""Assert spec.physical is False."""
|
||||
assert context.spec.physical is False, (
|
||||
f"Expected spec.physical to be False, got {context.spec.physical}"
|
||||
)
|
||||
|
||||
|
||||
@then("the spec should be a physical type")
|
||||
def step_spec_is_physical(context: Any) -> None:
|
||||
"""Assert spec is a physical type."""
|
||||
assert context.spec.physical is True, "Expected physical type"
|
||||
|
||||
|
||||
@then("the spec should be a virtual type")
|
||||
def step_spec_is_virtual(context: Any) -> None:
|
||||
"""Assert spec is a virtual type."""
|
||||
assert context.spec.physical is False, "Expected virtual type"
|
||||
|
||||
|
||||
@then("the resulting spec physical should be True")
|
||||
def step_result_physical_true(context: Any) -> None:
|
||||
"""Assert resulting spec.physical is True."""
|
||||
assert context.spec.physical is True, (
|
||||
f"Expected physical=True, got {context.spec.physical}"
|
||||
)
|
||||
|
||||
|
||||
@then("the resulting spec physical should be False")
|
||||
def step_result_physical_false(context: Any) -> None:
|
||||
"""Assert resulting spec.physical is False."""
|
||||
assert context.spec.physical is False, (
|
||||
f"Expected physical=False, got {context.spec.physical}"
|
||||
)
|
||||
|
||||
|
||||
@then("the cli dict should contain physical True")
|
||||
def step_cli_dict_physical_true(context: Any) -> None:
|
||||
"""Assert cli dict contains physical: True."""
|
||||
assert "physical" in context.cli_dict, "cli_dict missing 'physical' key"
|
||||
assert context.cli_dict["physical"] is True, (
|
||||
f"Expected physical=True in cli_dict, got {context.cli_dict.get('physical')}"
|
||||
)
|
||||
|
||||
|
||||
@then("the cli dict should not contain resource_kind")
|
||||
def step_cli_dict_no_resource_kind(context: Any) -> None:
|
||||
"""Assert cli dict does not contain resource_kind."""
|
||||
assert "resource_kind" not in context.cli_dict, (
|
||||
"cli_dict should not contain 'resource_kind' (deprecated field)"
|
||||
)
|
||||
|
||||
|
||||
@then("the schema physical field should be True")
|
||||
def step_schema_physical_true(context: Any) -> None:
|
||||
"""Assert schema.physical is True."""
|
||||
assert context.schema is not None, "Schema should not be None"
|
||||
assert context.schema.physical is True, (
|
||||
f"Expected schema.physical=True, got {context.schema.physical}"
|
||||
)
|
||||
|
||||
|
||||
@then("a validation error should be raised")
|
||||
def step_validation_error_raised(context: Any) -> None:
|
||||
"""Assert a validation error was raised."""
|
||||
assert context.validation_error is not None, (
|
||||
"Expected a validation error but none was raised"
|
||||
)
|
||||
|
||||
|
||||
@then("the rds type should exist")
|
||||
def step_rds_type_exists(context: Any) -> None:
|
||||
"""Assert aws-rds-instance type exists."""
|
||||
assert context.type_def is not None, (
|
||||
f"Resource type '{context.type_name}' not found in registry"
|
||||
)
|
||||
|
||||
|
||||
@then("the rds type physical should be True")
|
||||
def step_rds_physical_true(context: Any) -> None:
|
||||
"""Assert aws-rds-instance has physical=True."""
|
||||
assert context.type_def.get("physical") is True, (
|
||||
f"Expected physical=True for {context.type_name}, "
|
||||
f"got {context.type_def.get('physical')}"
|
||||
)
|
||||
|
||||
|
||||
@then('the rds type should inherit from "{parent}"')
|
||||
def step_rds_inherits(context: Any, parent: str) -> None:
|
||||
"""Assert aws-rds-instance inherits from the given parent."""
|
||||
assert context.type_def.get("inherits") == parent, (
|
||||
f"Expected {context.type_name} to inherit from '{parent}', "
|
||||
f"got '{context.type_def.get('inherits')}'"
|
||||
)
|
||||
|
||||
|
||||
@then('the rds type should have cli arg "{arg_name}"')
|
||||
def step_rds_has_cli_arg(context: Any, arg_name: str) -> None:
|
||||
"""Assert aws-rds-instance has the given CLI arg."""
|
||||
cli_args = context.type_def.get("cli_args", [])
|
||||
arg_names = [a["name"] for a in cli_args]
|
||||
assert arg_name in arg_names, (
|
||||
f"Expected {context.type_name} to have cli arg '{arg_name}', got {arg_names}"
|
||||
)
|
||||
|
||||
|
||||
@then('the rds type cli arg "{arg_name}" should be required')
|
||||
def step_rds_arg_required(context: Any, arg_name: str) -> None:
|
||||
"""Assert the given CLI arg is required."""
|
||||
cli_args = context.type_def.get("cli_args", [])
|
||||
arg = next((a for a in cli_args if a["name"] == arg_name), None)
|
||||
assert arg is not None, f"CLI arg '{arg_name}' not found"
|
||||
assert arg.get("required") is True, f"Expected cli arg '{arg_name}' to be required"
|
||||
|
||||
|
||||
@then('the rds type cli arg "{arg_name}" should not be required')
|
||||
def step_rds_arg_not_required(context: Any, arg_name: str) -> None:
|
||||
"""Assert the given CLI arg is not required."""
|
||||
cli_args = context.type_def.get("cli_args", [])
|
||||
arg = next((a for a in cli_args if a["name"] == arg_name), None)
|
||||
assert arg is not None, f"CLI arg '{arg_name}' not found"
|
||||
assert arg.get("required") is not True, (
|
||||
f"Expected cli arg '{arg_name}' to not be required"
|
||||
)
|
||||
|
||||
|
||||
@then("the cloud-database type should exist")
|
||||
def step_cloud_db_exists(context: Any) -> None:
|
||||
"""Assert cloud-database type exists."""
|
||||
assert context.type_def is not None, (
|
||||
f"Resource type '{context.type_name}' not found in registry"
|
||||
)
|
||||
|
||||
|
||||
@then("the cloud-database type physical should be True")
|
||||
def step_cloud_db_physical(context: Any) -> None:
|
||||
"""Assert cloud-database has physical=True."""
|
||||
assert context.type_def.get("physical") is True, (
|
||||
f"Expected physical=True for {context.type_name}"
|
||||
)
|
||||
|
||||
|
||||
@then('the s3 type should have cli arg "{arg_name}"')
|
||||
def step_s3_has_cli_arg(context: Any, arg_name: str) -> None:
|
||||
"""Assert aws-s3-bucket has the given CLI arg."""
|
||||
cli_args = context.type_def.get("cli_args", [])
|
||||
arg_names = [a["name"] for a in cli_args]
|
||||
assert arg_name in arg_names, (
|
||||
f"Expected {context.type_name} to have cli arg '{arg_name}', got {arg_names}"
|
||||
)
|
||||
|
||||
|
||||
@then('the s3 type cli arg "{arg_name}" should be required')
|
||||
def step_s3_arg_required(context: Any, arg_name: str) -> None:
|
||||
"""Assert the given S3 CLI arg is required."""
|
||||
cli_args = context.type_def.get("cli_args", [])
|
||||
arg = next((a for a in cli_args if a["name"] == arg_name), None)
|
||||
assert arg is not None, f"CLI arg '{arg_name}' not found"
|
||||
assert arg.get("required") is True, f"Expected cli arg '{arg_name}' to be required"
|
||||
|
||||
|
||||
@then("all physical types should have physical True")
|
||||
def step_all_physical_have_true(context: Any) -> None:
|
||||
"""Assert all physical types have physical=True."""
|
||||
for t in context.all_types:
|
||||
if t.get("physical") is True:
|
||||
assert t.get("physical") is True, (
|
||||
f"Type '{t['name']}' has physical={t.get('physical')}"
|
||||
)
|
||||
|
||||
|
||||
@then("no type should have a resource_kind field")
|
||||
def step_no_resource_kind_field(context: Any) -> None:
|
||||
"""Assert no built-in type has a resource_kind field."""
|
||||
for t in context.all_types:
|
||||
assert "resource_kind" not in t, (
|
||||
f"Type '{t['name']}' still has deprecated 'resource_kind' field"
|
||||
)
|
||||
|
||||
|
||||
@then("all virtual types should have physical False")
|
||||
def step_all_virtual_have_false(context: Any) -> None:
|
||||
"""Assert all virtual types have physical=False."""
|
||||
for t in context.all_types:
|
||||
if t.get("physical") is False:
|
||||
assert t.get("physical") is False, (
|
||||
f"Type '{t['name']}' has physical={t.get('physical')}"
|
||||
)
|
||||
@@ -222,7 +222,7 @@ def step_vcore_schema_name(context: object, name: str) -> None:
|
||||
assert _ctx(context).schema.name == name
|
||||
|
||||
|
||||
@then('the vcore schema resource_kind should be "{kind}"')
|
||||
@then("the vcore schema physical should be {value}")
|
||||
def step_vcore_schema_kind(context: object, kind: str) -> None:
|
||||
assert _ctx(context).schema.resource_kind == kind
|
||||
|
||||
@@ -375,7 +375,7 @@ def step_found_builtin_not_addable(context: object) -> None:
|
||||
)
|
||||
|
||||
|
||||
@then('the found builtin type resource_kind should be "{kind}" for vcore')
|
||||
@then("the found builtin type physical should be {value} for vcore")
|
||||
def step_found_builtin_kind(context: object, kind: str) -> None:
|
||||
entry: dict[str, Any] = _ctx(context).found_builtin
|
||||
assert entry["resource_kind"] == kind, (
|
||||
@@ -411,7 +411,7 @@ def step_virtual_config_missing_equiv(context: object) -> None:
|
||||
"""Create a virtual type config dict without equivalence."""
|
||||
_ctx(context).test_config = {
|
||||
"name": "test-virtual-no-equiv",
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -432,7 +432,7 @@ def step_virtual_config_empty_criteria(context: object) -> None:
|
||||
"""Create a virtual type config with equivalence but empty criteria."""
|
||||
_ctx(context).test_config = {
|
||||
"name": "test-virtual-empty-criteria",
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
|
||||
@@ -13,14 +13,14 @@ Contextual Binding Resolves Single Resource
|
||||
... from unittest.mock import MagicMock
|
||||
... from cleveragents.core.exceptions import NotFoundError
|
||||
... from cleveragents.domain.models.core.resource import Resource, PhysVirt
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.project import NamespacedProject, LinkedResource
|
||||
... from cleveragents.domain.models.core.tool import Tool, ToolSource, ToolType, ResourceSlot, ResourceAccessMode, BindingMode
|
||||
... from cleveragents.application.services.binding_resolution_service import BindingResolutionService
|
||||
... registry = MagicMock()
|
||||
... res = Resource(resource_id="01HGZ6FE0AQDYTR4BX00000001", name=None, resource_type_name="git-checkout", classification=PhysVirt.PHYSICAL)
|
||||
... registry.show_resource = lambda x: res
|
||||
... registry.show_type = lambda x: ResourceTypeSpec(name="git-checkout", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, built_in=True)
|
||||
... registry.show_type = lambda x: ResourceTypeSpec(name="git-checkout", physical=True, sandbox_strategy=SandboxStrategy.NONE, built_in=True)
|
||||
... project = NamespacedProject(name="proj", namespace="local", linked_resources=[LinkedResource(resource_id="01HGZ6FE0AQDYTR4BX00000001", alias="repo")])
|
||||
... tool = Tool(name="local/reader", description="Read", source=ToolSource.BUILTIN, tool_type=ToolType.TOOL, resource_slots=[ResourceSlot(name="repo", resource_type="git-checkout", access=ResourceAccessMode.READ_WRITE, binding=BindingMode.CONTEXTUAL)])
|
||||
... svc = BindingResolutionService(registry)
|
||||
@@ -40,14 +40,14 @@ Static Binding Resolves Named Resource
|
||||
... from unittest.mock import MagicMock
|
||||
... from cleveragents.core.exceptions import NotFoundError
|
||||
... from cleveragents.domain.models.core.resource import Resource, PhysVirt
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.project import NamespacedProject
|
||||
... from cleveragents.domain.models.core.tool import Tool, ToolSource, ToolType, ResourceSlot, ResourceAccessMode, BindingMode
|
||||
... from cleveragents.application.services.binding_resolution_service import BindingResolutionService
|
||||
... registry = MagicMock()
|
||||
... res = Resource(resource_id="01HGZ6FE0AQDYTR4BX00000020", name="local/shared-config", resource_type_name="fs-directory", classification=PhysVirt.PHYSICAL)
|
||||
... registry.show_resource = lambda x: res
|
||||
... registry.show_type = lambda x: ResourceTypeSpec(name="fs-directory", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, built_in=True)
|
||||
... registry.show_type = lambda x: ResourceTypeSpec(name="fs-directory", physical=True, sandbox_strategy=SandboxStrategy.NONE, built_in=True)
|
||||
... project = NamespacedProject(name="proj", namespace="local")
|
||||
... tool = Tool(name="local/deployer", description="Deploy", source=ToolSource.BUILTIN, tool_type=ToolType.TOOL, resource_slots=[ResourceSlot(name="config_dir", resource_type="fs-directory", access=ResourceAccessMode.READ_ONLY, binding=BindingMode.STATIC, static_resource="local/shared-config")])
|
||||
... svc = BindingResolutionService(registry)
|
||||
|
||||
@@ -46,7 +46,7 @@ def cmd_check_registration() -> None:
|
||||
assert cn in names, f"{cn} not in BUILTIN_TYPES: {names}"
|
||||
assert cn in ResourceTypeSpec.BUILTIN_NAMES, f"{cn} not in BUILTIN_NAMES"
|
||||
entry = next(d for d in BUILTIN_TYPES if d["name"] == cn)
|
||||
assert entry["resource_kind"] == "physical"
|
||||
assert entry["physical"] is True
|
||||
assert entry["built_in"] is True
|
||||
print("check-registration-ok")
|
||||
|
||||
@@ -101,7 +101,7 @@ def cmd_check_yaml_loading() -> None:
|
||||
assert path.exists(), f"Missing YAML: {path}"
|
||||
schema = ResourceTypeConfigSchema.from_yaml_file(path)
|
||||
assert schema.name == cn
|
||||
assert schema.resource_kind == "physical"
|
||||
assert schema.physical is True
|
||||
spec = ResourceTypeSpec.from_config(schema.model_dump())
|
||||
assert spec.name == cn
|
||||
print("check-yaml-loading-ok")
|
||||
|
||||
@@ -51,8 +51,8 @@ def _db_roundtrip() -> None:
|
||||
for name in ("executable", "lsp-server", "lsp-workspace", "lsp-document"):
|
||||
spec = svc.show_type(name)
|
||||
assert spec is not None, f"{name} not found after bootstrap"
|
||||
assert str(spec.resource_kind) == "physical", (
|
||||
f"{name} kind: {spec.resource_kind}"
|
||||
assert spec.physical is True, (
|
||||
f"{name} kind: physical={spec.physical}"
|
||||
)
|
||||
assert str(spec.sandbox_strategy) == "none", (
|
||||
f"{name} strategy: {spec.sandbox_strategy}"
|
||||
|
||||
@@ -16,7 +16,7 @@ Link Child And Verify Tree
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
|
||||
... engine = create_engine("sqlite:///:memory:")
|
||||
... @event.listens_for(engine, "connect")
|
||||
@@ -26,8 +26,8 @@ Link Child And Verify Tree
|
||||
... shared_session = factory()
|
||||
... rt_repo = ResourceTypeRepository(lambda: shared_session)
|
||||
... res_repo = ResourceRepository(lambda: shared_session)
|
||||
... parent_spec = ResourceTypeSpec(name="robot/dag-parent", description="Parent", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/dag-child"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... child_spec = ResourceTypeSpec(name="robot/dag-child", description="Child", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... parent_spec = ResourceTypeSpec(name="robot/dag-parent", description="Parent", physical=True, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/dag-child"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... child_spec = ResourceTypeSpec(name="robot/dag-child", description="Child", physical=True, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... rt_repo.create(parent_spec)
|
||||
... rt_repo.create(child_spec)
|
||||
... p = Resource(resource_id="01HDAGR0B0T0000000PARENT01", name=None, resource_type_name="robot/dag-parent", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
|
||||
@@ -52,7 +52,7 @@ Cycle Detection Rejects A To B To A
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository, CycleDetectedError
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
|
||||
... engine = create_engine("sqlite:///:memory:")
|
||||
... @event.listens_for(engine, "connect")
|
||||
@@ -62,7 +62,7 @@ Cycle Detection Rejects A To B To A
|
||||
... shared_session = factory()
|
||||
... rt_repo = ResourceTypeRepository(lambda: shared_session)
|
||||
... res_repo = ResourceRepository(lambda: shared_session)
|
||||
... spec = ResourceTypeSpec(name="robot/cycle-type", description="Cycle", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/cycle-type"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... spec = ResourceTypeSpec(name="robot/cycle-type", description="Cycle", physical=True, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/cycle-type"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... rt_repo.create(spec)
|
||||
... a = Resource(resource_id="01HDAGCYC000000000000000A1", name=None, resource_type_name="robot/cycle-type", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
|
||||
... b = Resource(resource_id="01HDAGCYC000000000000000B1", name=None, resource_type_name="robot/cycle-type", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
|
||||
@@ -89,7 +89,7 @@ Auto Discover Children
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
|
||||
... engine = create_engine("sqlite:///:memory:")
|
||||
... @event.listens_for(engine, "connect")
|
||||
@@ -99,8 +99,8 @@ Auto Discover Children
|
||||
... shared_session = factory()
|
||||
... rt_repo = ResourceTypeRepository(lambda: shared_session)
|
||||
... res_repo = ResourceRepository(lambda: shared_session)
|
||||
... parent_spec = ResourceTypeSpec(name="robot/disc-parent", description="Discoverer", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/disc-child"], auto_discovery={"enabled": True, "rules": [{"type": "robot/disc-child", "pattern": "*"}]}, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... child_spec = ResourceTypeSpec(name="robot/disc-child", description="Discovered", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... parent_spec = ResourceTypeSpec(name="robot/disc-parent", description="Discoverer", physical=True, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/disc-child"], auto_discovery={"enabled": True, "rules": [{"type": "robot/disc-child", "pattern": "*"}]}, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... child_spec = ResourceTypeSpec(name="robot/disc-child", description="Discovered", physical=True, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... rt_repo.create(parent_spec)
|
||||
... rt_repo.create(child_spec)
|
||||
... p = Resource(resource_id="01HDAGR0B0TDSC00PARENT0001", name=None, resource_type_name="robot/disc-parent", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
|
||||
|
||||
@@ -14,14 +14,14 @@ ResourceTypeRepository Create And Get
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base, ResourceTypeModel
|
||||
... from cleveragents.infrastructure.database.repositories import ResourceTypeRepository
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... engine = create_engine("sqlite:///:memory:")
|
||||
... @event.listens_for(engine, "connect")
|
||||
... def _fk(conn, _): conn.cursor().execute("PRAGMA foreign_keys=ON")
|
||||
... Base.metadata.create_all(engine)
|
||||
... factory = sessionmaker(bind=engine)
|
||||
... repo = ResourceTypeRepository(factory)
|
||||
... spec = ResourceTypeSpec(name="robot/test-type", description="Robot test", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.GIT_WORKTREE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... spec = ResourceTypeSpec(name="robot/test-type", description="Robot test", physical=True, sandbox_strategy=SandboxStrategy.GIT_WORKTREE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... repo.create(spec)
|
||||
... result = repo.get("robot/test-type")
|
||||
... assert result is not None, "Resource type not found"
|
||||
@@ -40,7 +40,7 @@ ResourceRepository Create And Resolve
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, SandboxStrategy
|
||||
... from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
|
||||
... engine = create_engine("sqlite:///:memory:")
|
||||
... @event.listens_for(engine, "connect")
|
||||
@@ -49,7 +49,7 @@ ResourceRepository Create And Resolve
|
||||
... factory = sessionmaker(bind=engine)
|
||||
... rt_repo = ResourceTypeRepository(factory)
|
||||
... res_repo = ResourceRepository(factory)
|
||||
... spec = ResourceTypeSpec(name="robot/res-type", description="Type", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... spec = ResourceTypeSpec(name="robot/res-type", description="Type", physical=True, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
|
||||
... rt_repo.create(spec)
|
||||
... res = Resource(resource_id="01HGZ6FE0AQDYTR4BX00000001", name="robot/test-res", resource_type_name="robot/res-type", classification=PhysVirt.PHYSICAL, description="Test", properties={}, location="/tmp/test", content_hash=None, sandbox_strategy=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
|
||||
... res_repo.create(res)
|
||||
|
||||
@@ -27,7 +27,7 @@ Bootstrap Seeds Fs Directory Type Into Registry
|
||||
... assert "fs-directory" in registered, f"fs-directory not in registered: {registered}"
|
||||
... spec = service.show_type("fs-directory")
|
||||
... assert spec.name == "fs-directory", f"name mismatch: {spec.name}"
|
||||
... kind = spec.resource_kind.value if hasattr(spec.resource_kind, "value") else str(spec.resource_kind)
|
||||
... kind = "physical" if spec.physical else "virtual"
|
||||
... assert kind == "physical", f"kind: {kind}"
|
||||
... strategy = spec.sandbox_strategy.value if hasattr(spec.sandbox_strategy, "value") else str(spec.sandbox_strategy)
|
||||
... assert strategy == "copy_on_write", f"strategy: {strategy}"
|
||||
|
||||
@@ -27,7 +27,7 @@ Bootstrap Seeds Fs Mount Type Into Registry
|
||||
... assert "fs-mount" in registered, f"fs-mount not in registered: {registered}"
|
||||
... spec = service.show_type("fs-mount")
|
||||
... assert spec.name == "fs-mount", f"name mismatch: {spec.name}"
|
||||
... kind = spec.resource_kind.value if hasattr(spec.resource_kind, "value") else str(spec.resource_kind)
|
||||
... kind = "physical" if spec.physical else "virtual"
|
||||
... assert kind == "physical", f"kind: {kind}"
|
||||
... strategy = spec.sandbox_strategy.value if hasattr(spec.sandbox_strategy, "value") else str(spec.sandbox_strategy)
|
||||
... assert strategy == "copy_on_write", f"strategy: {strategy}"
|
||||
|
||||
@@ -55,7 +55,7 @@ Git Checkout Type Exists After Bootstrap
|
||||
... assert "git-checkout" in registered, f"git-checkout not in registered: {registered}"
|
||||
... spec = service.show_type("git-checkout")
|
||||
... assert spec.name == "git-checkout", f"name mismatch: {spec.name}"
|
||||
... rk = spec.resource_kind.value if hasattr(spec.resource_kind, "value") else str(spec.resource_kind)
|
||||
... rk = "physical" if spec.physical else "virtual"
|
||||
... assert rk == "physical", f"kind mismatch: {rk}"
|
||||
... ss = spec.sandbox_strategy.value if hasattr(spec.sandbox_strategy, "value") else str(spec.sandbox_strategy)
|
||||
... assert ss == "git_worktree", f"sandbox mismatch: {ss}"
|
||||
|
||||
@@ -15,7 +15,7 @@ Load Git Checkout Built-in YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${GIT_CHECKOUT_YAML}")
|
||||
... assert schema.name == "git-checkout", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "physical", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is True, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "git_worktree", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert len(schema.cli_args) == 2, f"cli_args: {len(schema.cli_args)}"
|
||||
@@ -31,7 +31,7 @@ Load Fs Directory Built-in YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${FS_DIRECTORY_YAML}")
|
||||
... assert schema.name == "fs-directory", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "physical", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is True, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "copy_on_write", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert len(schema.cli_args) == 1, f"cli_args: {len(schema.cli_args)}"
|
||||
|
||||
@@ -30,7 +30,7 @@ Load All Git Taxonomy YAMLs And Validate
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}config = yaml.safe_load(f)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}spec = ResourceTypeSpec.from_config(config)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.name == name, f"name: {spec.name}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.resource_kind.value == "physical", f"kind: {spec.resource_kind}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.physical is True, f"kind: physical={spec.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.sandbox_strategy.value == sandbox, f"sandbox: {spec.sandbox_strategy}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.user_addable == addable, f"addable: {spec.user_addable}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.built_in is True, f"built_in: {spec.built_in}"
|
||||
@@ -52,7 +52,7 @@ Load Filesystem Link YAMLs And Validate
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}config = yaml.safe_load(f)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}spec = ResourceTypeSpec.from_config(config)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.name == name, f"name: {spec.name}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.resource_kind.value == "physical"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.physical is True, f"kind: physical={spec.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.sandbox_strategy.value == "copy_on_write"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.user_addable is False
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.built_in is True
|
||||
|
||||
@@ -16,7 +16,7 @@ Load Remote Virtual Type YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${REMOTE_YAML}")
|
||||
... assert schema.name == "remote", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "virtual", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is False, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "none", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert schema.user_addable is False, f"user_addable: {schema.user_addable}"
|
||||
@@ -35,7 +35,7 @@ Load Submodule Virtual Type YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${SUBMODULE_YAML}")
|
||||
... assert schema.name == "submodule", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "virtual", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is False, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "none", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert schema.user_addable is False, f"user_addable: {schema.user_addable}"
|
||||
@@ -55,7 +55,7 @@ Load Symlink Virtual Type YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${SYMLINK_YAML}")
|
||||
... assert schema.name == "symlink", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "virtual", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is False, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "none", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert schema.user_addable is False, f"user_addable: {schema.user_addable}"
|
||||
@@ -80,7 +80,7 @@ Load All Deferred Virtual YAMLs As Domain Models
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}config = yaml.safe_load(f)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}spec = ResourceTypeSpec.from_config(config)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.name == expected_name, f"name mismatch: {spec.name}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert str(spec.resource_kind) == "virtual", f"kind: {spec.resource_kind}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.physical is False, f"kind: physical={spec.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.equivalence is not None, "equivalence missing"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.equivalence["criteria"] == expected_criteria
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}cli_dict = spec.as_cli_dict()
|
||||
@@ -107,7 +107,7 @@ Virtual Types Survive DB Bootstrap Roundtrip
|
||||
... for name, expected_criteria in [("remote", ["url"]), ("submodule", ["url", "path"]), ("symlink", ["name", "target_path"])]:
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}spec = svc.show_type(name)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec is not None, f"{name} not found after bootstrap"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert str(spec.resource_kind.value) == "virtual", f"{name} kind: {spec.resource_kind}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.physical is False, f"{name} kind: physical={spec.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.user_addable is False, f"{name} user_addable: {spec.user_addable}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.equivalence is not None, f"{name} missing equivalence"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.equivalence["criteria"] == expected_criteria, f"{name} criteria: {spec.equivalence}"
|
||||
|
||||
@@ -16,7 +16,7 @@ Load Fs Mount Built-in YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${FS_MOUNT_YAML}")
|
||||
... assert schema.name == "fs-mount", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "physical", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is True, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "copy_on_write", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert schema.user_addable is True, f"user_addable: {schema.user_addable}"
|
||||
@@ -38,7 +38,7 @@ Load Fs File Built-in YAML And Validate
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${FS_FILE_YAML}")
|
||||
... assert schema.name == "fs-file", f"name: {schema.name}"
|
||||
... assert schema.resource_kind == "physical", f"kind: {schema.resource_kind}"
|
||||
... assert schema.physical is True, f"kind: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "copy_on_write", f"strategy: {schema.sandbox_strategy}"
|
||||
... assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
... assert schema.user_addable is False, f"user_addable: {schema.user_addable}"
|
||||
|
||||
@@ -16,7 +16,7 @@ Load Resource Type YAML Fixture And Assert Fields
|
||||
... from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
... schema = ResourceTypeConfigSchema.from_yaml_file("${FIXTURE_YAML}")
|
||||
... assert schema.name == "myorg/robot-test", f"name mismatch: {schema.name}"
|
||||
... assert schema.resource_kind == "physical", f"kind mismatch: {schema.resource_kind}"
|
||||
... assert schema.physical is True, f"kind mismatch: physical={schema.physical}"
|
||||
... assert schema.sandbox_strategy == "git_worktree", f"strategy mismatch: {schema.sandbox_strategy}"
|
||||
... assert len(schema.cli_args) == 2, f"cli_args count mismatch: {len(schema.cli_args)}"
|
||||
... assert schema.cli_args[0].name == "path", f"first arg name mismatch: {schema.cli_args[0].name}"
|
||||
@@ -38,7 +38,7 @@ Resource Type Domain Model From Config
|
||||
... f.close()
|
||||
... spec = ResourceTypeSpec.from_config(config)
|
||||
... assert spec.name == "myorg/robot-test", f"name: {spec.name}"
|
||||
... assert spec.resource_kind.value == "physical"
|
||||
... assert spec.physical is True, f"kind mismatch: physical={spec.physical}"
|
||||
... assert spec.sandbox_strategy.value == "git_worktree"
|
||||
... cli_dict = spec.as_cli_dict()
|
||||
... assert "name" in cli_dict
|
||||
|
||||
@@ -28,7 +28,7 @@ Load All Virtual Core YAMLs And Validate Schema
|
||||
... for name, path in VIRTUAL_TYPES.items():
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}schema = ResourceTypeConfigSchema.from_yaml_file(path)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert schema.name == name, f"name mismatch: {schema.name} != {name}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert schema.resource_kind == "virtual", f"kind: {schema.resource_kind}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert schema.physical is False, f"kind: physical={schema.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert schema.sandbox_strategy == "none", f"strategy: {schema.sandbox_strategy}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert schema.user_addable is False, f"addable: {schema.user_addable}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert schema.built_in is True, f"built_in: {schema.built_in}"
|
||||
@@ -43,7 +43,7 @@ Load Virtual Core YAMLs As Domain Models
|
||||
[Documentation] Load all virtual core YAMLs as ResourceTypeSpec domain models
|
||||
${script}= Catenate SEPARATOR=\n
|
||||
... import yaml
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind
|
||||
... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec
|
||||
... PATHS = {
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}"file": "${FILE_YAML}",
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}"directory": "${DIR_YAML}",
|
||||
@@ -57,7 +57,7 @@ Load Virtual Core YAMLs As Domain Models
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}config = yaml.safe_load(f)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}spec = ResourceTypeSpec.from_config(config)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.name == name, f"name: {spec.name}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.resource_kind == ResourceKind.VIRTUAL
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.physical is False, f"kind: physical={spec.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}cli_dict = spec.as_cli_dict()
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert "equivalence" in cli_dict, f"no equivalence in cli_dict for {name}"
|
||||
... print("All 6 virtual core domain models validated successfully")
|
||||
@@ -74,7 +74,7 @@ Verify Virtual Types In Bootstrap Registration
|
||||
... for vt in expected:
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert vt in names, f"{vt} not in builtin types: {names}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}entry = next(d for d in BUILTIN_TYPES if d["name"] == vt)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert entry["resource_kind"] == "virtual", f"{vt} kind: {entry['resource_kind']}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert entry["physical"] is False, f"{vt} kind: physical={entry.get('physical')}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert entry["user_addable"] is False, f"{vt} addable: {entry['user_addable']}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert entry["sandbox_strategy"] == "none", f"{vt} strategy: {entry['sandbox_strategy']}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert entry.get("equivalence") is not None, f"{vt} missing equivalence"
|
||||
@@ -113,7 +113,7 @@ Virtual Types Appear In CLI Resource Type List
|
||||
... for vt in expected:
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert vt in type_names, f"{vt} not in listed types: {type_names}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}spec = next(t for t in types if t.name == vt)
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert str(spec.resource_kind) == "virtual", f"{vt} kind: {spec.resource_kind}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.physical is False, f"{vt} kind: physical={spec.physical}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.user_addable is False, f"{vt} user_addable: {spec.user_addable}"
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}assert spec.equivalence is not None, f"{vt} missing equivalence"
|
||||
... print(f"CLI list path: all 6 virtual types present and correctly typed")
|
||||
|
||||
@@ -5,11 +5,10 @@ placeholders. Extracted from ``_resource_registry_data.py``.
|
||||
|
||||
.. note::
|
||||
|
||||
This module is 754 lines, exceeding the 500-line CONTRIBUTING
|
||||
guideline. The overage is accepted because splitting the 36 AWS
|
||||
type definitions mid-file would break the ``_aws_type()`` helper
|
||||
pattern. A follow-up extraction to ``_resource_registry_cloud_aws``
|
||||
is tracked but not blocking.
|
||||
This module exceeds the 500-line CONTRIBUTING guideline. The overage
|
||||
is accepted because splitting the AWS type definitions mid-file would
|
||||
break the ``_aws_type()`` helper pattern. A follow-up extraction to
|
||||
``_resource_registry_cloud_aws`` is tracked but not blocking.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -47,7 +46,7 @@ def _cloud_base(
|
||||
return {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -74,7 +73,7 @@ def _aws_type(
|
||||
d: dict[str, Any] = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": user_addable,
|
||||
"built_in": True,
|
||||
@@ -93,7 +92,7 @@ def _aws_type(
|
||||
# Built-in resource type definitions (registered at startup)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# IMPORTANT: Order matters — children must appear after their parents so
|
||||
# IMPORTANT: Order matters -- children must appear after their parents so
|
||||
# that ``bootstrap_builtin_types()`` can resolve ``inherits`` references.
|
||||
|
||||
# ===== Git / Filesystem / Container types (unchanged) =====
|
||||
@@ -102,7 +101,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "git-checkout",
|
||||
"description": "A local git checkout (cloned repository or worktree).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "git_worktree",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -140,7 +139,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "fs-directory",
|
||||
"description": "A local filesystem directory.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -182,7 +181,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "container-instance",
|
||||
"description": "A container execution environment instance.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -211,7 +210,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
"A devcontainer execution environment auto-discovered "
|
||||
"from .devcontainer/ configuration."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -246,7 +245,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "devcontainer-file",
|
||||
"description": "A single devcontainer.json configuration file resource.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -268,7 +267,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
#
|
||||
# Abstract base types that define common cloud concepts. Provider-
|
||||
# specific types (``aws-*``, ``azure-*``, etc.) inherit from these.
|
||||
# Not user-addable — users create provider-specific account types.
|
||||
# Not user-addable -- users create provider-specific account types.
|
||||
|
||||
CLOUD_BASE_TYPES: list[dict[str, Any]] = [
|
||||
# -- Account & region structure --
|
||||
@@ -311,6 +310,11 @@ CLOUD_BASE_TYPES: list[dict[str, Any]] = [
|
||||
"cloud-block-storage",
|
||||
"Abstract base: block storage volume.",
|
||||
),
|
||||
# -- Database --
|
||||
_cloud_base(
|
||||
"cloud-database",
|
||||
"Abstract base: a managed relational or NoSQL database instance.",
|
||||
),
|
||||
# -- IAM --
|
||||
_cloud_base(
|
||||
"cloud-identity-principal",
|
||||
@@ -427,6 +431,7 @@ AWS_TYPES: list[dict[str, Any]] = [
|
||||
"aws-launch-template",
|
||||
"aws-asg",
|
||||
"aws-s3-bucket",
|
||||
"aws-rds-instance",
|
||||
"aws-ebs-volume",
|
||||
"aws-efs-filesystem",
|
||||
"aws-cloudwatch-log-group",
|
||||
@@ -544,6 +549,63 @@ AWS_TYPES: list[dict[str, Any]] = [
|
||||
"An AWS S3 object storage bucket.",
|
||||
inherits="cloud-object-store",
|
||||
parent_types=["aws-region"],
|
||||
cli_args=[
|
||||
{
|
||||
"name": "bucket-name",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": "S3 bucket name (globally unique).",
|
||||
},
|
||||
{
|
||||
"name": "region",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "AWS region where the bucket is located.",
|
||||
},
|
||||
{
|
||||
"name": "versioning",
|
||||
"type": "boolean",
|
||||
"required": False,
|
||||
"description": "Whether S3 versioning is enabled.",
|
||||
"default": None,
|
||||
},
|
||||
],
|
||||
capabilities=_CLOUD_CAPS_RW,
|
||||
),
|
||||
_aws_type(
|
||||
"aws-rds-instance",
|
||||
"An AWS RDS managed relational database instance.",
|
||||
inherits="cloud-database",
|
||||
parent_types=["aws-region"],
|
||||
cli_args=[
|
||||
{
|
||||
"name": "engine",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": "DB engine (mysql, postgres, mariadb, oracle).",
|
||||
},
|
||||
{
|
||||
"name": "instance-class",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": "RDS instance class (e.g. db.t3.micro).",
|
||||
},
|
||||
{
|
||||
"name": "db-name",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "Initial database name to create.",
|
||||
"default": None,
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer",
|
||||
"required": False,
|
||||
"description": "Database port (defaults to engine default).",
|
||||
"default": None,
|
||||
},
|
||||
],
|
||||
capabilities=_CLOUD_CAPS_RW,
|
||||
),
|
||||
_aws_type(
|
||||
"aws-ebs-volume",
|
||||
@@ -683,7 +745,7 @@ GCP_AZURE_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "gcp",
|
||||
"description": "Google Cloud Platform resource (hierarchy pending).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -716,7 +778,7 @@ GCP_AZURE_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "azure",
|
||||
"description": "Microsoft Azure cloud resource (hierarchy pending).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
|
||||
@@ -22,7 +22,7 @@ from typing import Any
|
||||
__all__ = ["CONTAINER_INFRA_TYPES"]
|
||||
|
||||
# Handler references per spec line 25096-25114. These handler classes
|
||||
# do not exist yet — the references are forward declarations that will
|
||||
# do not exist yet -- the references are forward declarations that will
|
||||
# be resolved when the container runtime integration is implemented.
|
||||
_RUNTIME_HANDLER = "cleveragents.resource.handlers.container:ContainerRuntimeHandler"
|
||||
_IMAGE_HANDLER = "cleveragents.resource.handlers.container:ContainerImageHandler"
|
||||
@@ -39,7 +39,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"A container runtime engine (Docker, Podman, containerd, etc.)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -103,7 +103,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "container-image",
|
||||
"description": ("A container image identified by tag and/or digest."),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -149,7 +149,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "container-mount",
|
||||
"description": ("A bind mount or volume mount inside a container instance."),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -173,7 +173,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
"Execution environment variables and configuration "
|
||||
"inside a container instance."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -196,7 +196,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"A port mapping between host and container (e.g. 8080:80/tcp)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -217,7 +217,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "container-volume",
|
||||
"description": ("A named container volume managed by the runtime engine."),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -254,7 +254,7 @@ CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"A container network (bridge, host, overlay, macvlan, or none)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Internal data helpers for the Resource Registry Service.
|
||||
|
||||
Contains the built-in type definitions and DB ↔ domain conversion
|
||||
Contains the built-in type definitions and DB <-> domain conversion
|
||||
functions. Extracted from ``resource_registry_service.py`` to keep
|
||||
individual modules under the 500-line CONTRIBUTING limit.
|
||||
|
||||
@@ -39,7 +39,6 @@ from cleveragents.application.services._resource_registry_virtual_deferred impor
|
||||
)
|
||||
from cleveragents.domain.models.core.resource import PhysVirt, Resource
|
||||
from cleveragents.domain.models.core.resource_type import (
|
||||
ResourceKind,
|
||||
ResourceTypeSpec,
|
||||
SandboxStrategy,
|
||||
)
|
||||
@@ -66,7 +65,7 @@ __all__ = [
|
||||
# Built-in resource type definitions (registered at startup)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# IMPORTANT: Order matters — children must appear after their parents so
|
||||
# IMPORTANT: Order matters -- children must appear after their parents so
|
||||
# that ``bootstrap_builtin_types()`` can resolve ``inherits`` references.
|
||||
|
||||
# ===== Git / Filesystem / Container types (unchanged) =====
|
||||
@@ -75,7 +74,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "fs-mount",
|
||||
"description": "A physical mount point on the local system.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -107,7 +106,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "git-checkout",
|
||||
"description": "A local git checkout (cloned repository or worktree).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "git_worktree",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -145,7 +144,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "fs-directory",
|
||||
"description": "A local filesystem directory.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -187,7 +186,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "container-instance",
|
||||
"description": "A container execution environment instance.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -249,7 +248,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
"A devcontainer execution environment auto-discovered "
|
||||
"from .devcontainer/ configuration."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -284,7 +283,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "devcontainer-file",
|
||||
"description": "A single devcontainer.json configuration file resource.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -306,7 +305,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
#
|
||||
# Abstract base types that define common cloud concepts. Provider-
|
||||
# specific types (``aws-*``, ``azure-*``, etc.) inherit from these.
|
||||
# Not user-addable — users create provider-specific account types.
|
||||
# Not user-addable -- users create provider-specific account types.
|
||||
|
||||
|
||||
# ===== Assemble final list =====
|
||||
@@ -332,7 +331,7 @@ BUILTIN_TYPES: list[dict[str, Any]] = [
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DB ↔ domain conversion helpers
|
||||
# DB <-> domain conversion helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -382,13 +381,15 @@ def spec_to_db(spec: ResourceTypeSpec, source: str) -> ResourceTypeModel:
|
||||
if spec.equivalence is not None:
|
||||
equivalence_json = json.dumps(spec.equivalence)
|
||||
|
||||
# Convert physical bool to resource_kind string for DB storage
|
||||
# (DB column still uses the legacy string format for backward compat)
|
||||
resource_kind_str = "physical" if spec.physical else "virtual"
|
||||
|
||||
return ResourceTypeModel(
|
||||
name=spec.name,
|
||||
namespace=namespace,
|
||||
description=spec.description or None,
|
||||
resource_kind=spec.resource_kind.value
|
||||
if isinstance(spec.resource_kind, ResourceKind)
|
||||
else spec.resource_kind,
|
||||
resource_kind=resource_kind_str,
|
||||
sandbox_strategy=spec.sandbox_strategy.value
|
||||
if isinstance(spec.sandbox_strategy, SandboxStrategy)
|
||||
else spec.sandbox_strategy,
|
||||
@@ -459,6 +460,8 @@ def db_to_spec(row: ResourceTypeModel) -> ResourceTypeSpec:
|
||||
raw_inherits = getattr(row, "inherits", None)
|
||||
inherits_str: str | None = str(raw_inherits) if raw_inherits is not None else None
|
||||
|
||||
# Pass resource_kind from DB for backward compat -- from_config handles
|
||||
# both "physical"/"virtual" string (legacy) and physical bool (new).
|
||||
return ResourceTypeSpec.from_config(
|
||||
{
|
||||
"name": name_str,
|
||||
|
||||
@@ -34,14 +34,14 @@ __all__ = ["LSP_RESOURCE_TYPES"]
|
||||
# - lsp-document: text document tracked by an LSP server
|
||||
|
||||
LSP_RESOURCE_TYPES: list[dict[str, Any]] = [
|
||||
# ── executable ───────────────────────────────────────────────
|
||||
# -- executable -----------------------------------------------
|
||||
{
|
||||
"name": "executable",
|
||||
"description": (
|
||||
"System executable (language runtime, compiler, LSP server "
|
||||
"binary). Discovered from container or host PATH."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -74,14 +74,14 @@ LSP_RESOURCE_TYPES: list[dict[str, Any]] = [
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
# ── lsp-server ───────────────────────────────────────────────
|
||||
# -- lsp-server -----------------------------------------------
|
||||
{
|
||||
"name": "lsp-server",
|
||||
"description": (
|
||||
"LSP server definition and running process. Provides "
|
||||
"semantic services for one or more languages."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -141,14 +141,14 @@ LSP_RESOURCE_TYPES: list[dict[str, Any]] = [
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
# ── lsp-workspace ────────────────────────────────────────────
|
||||
# -- lsp-workspace --------------------------------------------
|
||||
{
|
||||
"name": "lsp-workspace",
|
||||
"description": (
|
||||
"Workspace root tracked by an LSP server. Corresponds to "
|
||||
"a workspaceFolder in the LSP specification."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -167,14 +167,14 @@ LSP_RESOURCE_TYPES: list[dict[str, Any]] = [
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
# ── lsp-document ─────────────────────────────────────────────
|
||||
# -- lsp-document ---------------------------------------------
|
||||
{
|
||||
"name": "lsp-document",
|
||||
"description": (
|
||||
"Text document tracked by an LSP server. Provides semantic "
|
||||
"access: types, diagnostics, completions, references, symbols."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
|
||||
@@ -30,7 +30,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"A git repository \u2014 the object database, refs, and full history."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -84,7 +84,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
"A remote URL configured on a git repo "
|
||||
"(e.g., origin \u2192 https://github.com/org/repo)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -102,7 +102,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "git-branch",
|
||||
"description": "A named branch ref (e.g., main, feature/auth).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -126,12 +126,12 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
},
|
||||
{
|
||||
# NOTE: git-tag has no auto_discovery (unlike git-branch) because
|
||||
# tags are passive refs — they point to a commit but don't "own"
|
||||
# children that need discovery. The tag→commit relationship is
|
||||
# tags are passive refs -- they point to a commit but don't "own"
|
||||
# children that need discovery. The tag->commit relationship is
|
||||
# expressed via child_types only.
|
||||
"name": "git-tag",
|
||||
"description": "A tag ref \u2014 lightweight or annotated (e.g., v1.0.0).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -140,7 +140,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
# Spec S23518 omits git-commit as child of git-tag, but annotated tags
|
||||
# target commits directly. Added for DAG consistency with git-commit
|
||||
# listing git-tag as a parent.
|
||||
# NOTE: This is NOT a cycle — git-tag is a parent of git-commit
|
||||
# NOTE: This is NOT a cycle -- git-tag is a parent of git-commit
|
||||
# (tag -> commit), and git-commit lists git-tag as parent (commit
|
||||
# can be reached FROM a tag). The DAG remains acyclic.
|
||||
"child_types": ["git-commit"],
|
||||
@@ -155,7 +155,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "git-commit",
|
||||
"description": "A specific commit object.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -182,7 +182,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "git-tree",
|
||||
"description": "A tree object \u2014 a directory listing at a specific commit.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -215,7 +215,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
"A blob entry in a tree \u2014 a specific file's content "
|
||||
"at a specific path and mode."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -233,7 +233,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "git-stash",
|
||||
"description": "A stash entry (e.g., stash@{0}).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -254,7 +254,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
"A submodule reference \u2014 a pointer to another git "
|
||||
"repository at a specific commit and path."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -272,7 +272,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "fs-symlink",
|
||||
"description": "A symbolic link on the local filesystem.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -292,7 +292,7 @@ DEFERRED_PHYSICAL_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"A hard link on the local filesystem \u2014 a file with link count > 1."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "copy_on_write",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
|
||||
@@ -38,7 +38,7 @@ VIRTUAL_CORE_TYPES: list[dict[str, Any]] = [
|
||||
"Cross-layer file identity linking equivalent physical files "
|
||||
"by content hash, filename, and permissions."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -66,7 +66,7 @@ VIRTUAL_CORE_TYPES: list[dict[str, Any]] = [
|
||||
"Cross-layer directory identity linking equivalent physical "
|
||||
"directories by recursive content."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -98,7 +98,7 @@ VIRTUAL_CORE_TYPES: list[dict[str, Any]] = [
|
||||
"Cross-repo commit identity linking commits with "
|
||||
"the same hash across repositories."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -126,7 +126,7 @@ VIRTUAL_CORE_TYPES: list[dict[str, Any]] = [
|
||||
"Cross-repo branch identity linking branches with "
|
||||
"the same name and HEAD commit hash."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -153,7 +153,7 @@ VIRTUAL_CORE_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"Cross-repo tag identity linking tags with the same name and target object."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -180,7 +180,7 @@ VIRTUAL_CORE_TYPES: list[dict[str, Any]] = [
|
||||
"description": (
|
||||
"Cross-repo/cross-commit tree identity linking trees with the same hash."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
|
||||
@@ -33,7 +33,7 @@ VIRTUAL_DEFERRED_TYPES: list[dict[str, Any]] = [
|
||||
"Cross-repo remote identity. Links git-remote resources "
|
||||
"across different repos that point to the same URL."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -61,7 +61,7 @@ VIRTUAL_DEFERRED_TYPES: list[dict[str, Any]] = [
|
||||
"Cross-repo submodule identity. Links git-submodule resources "
|
||||
"across different repos with the same submodule URL and path."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
@@ -90,7 +90,7 @@ VIRTUAL_DEFERRED_TYPES: list[dict[str, Any]] = [
|
||||
"git-tree-entry (mode 120000) resources with the same "
|
||||
"name and target."
|
||||
),
|
||||
"resource_kind": "virtual",
|
||||
"physical": False,
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
|
||||
@@ -133,7 +133,7 @@ def _resource_type_dict(spec: Any) -> dict[str, object]:
|
||||
result: dict[str, object] = {
|
||||
"name": spec.name,
|
||||
"description": spec.description or "",
|
||||
"resource_kind": str(spec.resource_kind),
|
||||
"physical": spec.physical,
|
||||
"sandbox_strategy": str(spec.sandbox_strategy),
|
||||
"user_addable": spec.user_addable,
|
||||
"built_in": spec.built_in,
|
||||
@@ -384,7 +384,7 @@ def type_list(
|
||||
inherits = getattr(spec, "inherits", None) or ""
|
||||
table.add_row(
|
||||
spec.name,
|
||||
str(spec.resource_kind),
|
||||
"physical" if spec.physical else "virtual",
|
||||
str(spec.sandbox_strategy),
|
||||
inherits,
|
||||
"yes" if spec.built_in else "no",
|
||||
@@ -479,7 +479,7 @@ def _print_type_panel(spec: Any) -> None:
|
||||
f"[bold]Description:[/bold] {spec.description or '(none)'}\n"
|
||||
f"[bold]Inherits:[/bold] {inherits_display}\n"
|
||||
f"[bold]Inheritance Chain:[/bold] {chain_display}\n"
|
||||
f"[bold]Kind:[/bold] {spec.resource_kind}\n"
|
||||
f"[bold]Kind:[/bold] {'physical' if spec.physical else 'virtual'}\n"
|
||||
f"[bold]Sandbox Strategy:[/bold] {spec.sandbox_strategy}\n"
|
||||
f"[bold]Built-in:[/bold] {'yes' if spec.built_in else 'no'}\n"
|
||||
f"[bold]User-addable:[/bold] {'yes' if spec.user_addable else 'no'}\n"
|
||||
|
||||
@@ -44,7 +44,7 @@ from cleveragents.domain.models.core._resource_type_validation import (
|
||||
#: CLI-safe argument name: lowercase alphanumeric, hyphens, underscores.
|
||||
_CLI_ARG_NAME_RE = re.compile(r"^[a-z][a-z0-9_-]*$")
|
||||
|
||||
#: Namespaced name: ``namespace/name`` — both components must start with a letter.
|
||||
#: Namespaced name: ``namespace/name`` -- both components must start with a letter.
|
||||
_NAMESPACED_RE = re.compile(r"^[a-zA-Z][a-zA-Z0-9_-]*/[a-zA-Z][a-zA-Z0-9_-]*$")
|
||||
|
||||
#: Built-in (unnamespaced) name: simple alphanumeric + hyphens/underscores.
|
||||
@@ -62,6 +62,11 @@ _VALID_ARG_TYPES = frozenset({"string", "integer", "float", "boolean", "path"})
|
||||
class ResourceKind(StrEnum):
|
||||
"""Classification of a resource type as physical or virtual.
|
||||
|
||||
.. deprecated::
|
||||
Use the ``physical: bool`` field on :class:`ResourceTypeSpec` instead.
|
||||
``ResourceKind`` is retained for backward compatibility with legacy
|
||||
configuration files that still use ``resource_kind``.
|
||||
|
||||
Physical resources represent tangible assets (files, directories, repos).
|
||||
Virtual resources represent derived/computed assets (branches, tree entries).
|
||||
"""
|
||||
@@ -174,7 +179,7 @@ class ResourceTypeSpec(BaseModel):
|
||||
# Built-in resource type names (unnamespaced).
|
||||
# The actual frozenset lives in _resource_type_validation to keep
|
||||
# this module under the 500-line CONTRIBUTING limit.
|
||||
# Built-in resource type names — sourced from _resource_type_validation.py
|
||||
# Built-in resource type names -- sourced from _resource_type_validation.py
|
||||
# to keep this module under the 500-line CONTRIBUTING limit.
|
||||
BUILTIN_NAMES: ClassVar[frozenset[str]] = BUILTIN_TYPE_NAMES
|
||||
|
||||
@@ -187,9 +192,13 @@ class ResourceTypeSpec(BaseModel):
|
||||
"",
|
||||
description="Human-readable description of this resource type.",
|
||||
)
|
||||
resource_kind: ResourceKind = Field(
|
||||
physical: bool = Field(
|
||||
...,
|
||||
description="Physical or virtual classification.",
|
||||
description=(
|
||||
"true for physical types (concrete manifestations), "
|
||||
"false for virtual types (abstract identity linking equivalent "
|
||||
"physical resources). Replaces the legacy resource_kind enum field."
|
||||
),
|
||||
)
|
||||
sandbox_strategy: SandboxStrategy = Field(
|
||||
...,
|
||||
@@ -319,7 +328,7 @@ class ResourceTypeSpec(BaseModel):
|
||||
)
|
||||
|
||||
# Virtual type constraints (equivalence, no sandbox, etc.)
|
||||
if self.resource_kind == ResourceKind.VIRTUAL:
|
||||
if not self.physical:
|
||||
_validate_virtual_type(
|
||||
self.name,
|
||||
self.equivalence,
|
||||
@@ -355,6 +364,9 @@ class ResourceTypeSpec(BaseModel):
|
||||
def from_config(cls, config: dict[str, Any]) -> ResourceTypeSpec:
|
||||
"""Create a ResourceTypeSpec from a YAML configuration dict.
|
||||
|
||||
Supports both the new ``physical`` boolean field (per spec) and the
|
||||
legacy ``resource_kind`` string enum for backward compatibility.
|
||||
|
||||
Args:
|
||||
config: Parsed YAML configuration with resource type fields.
|
||||
|
||||
@@ -366,11 +378,29 @@ class ResourceTypeSpec(BaseModel):
|
||||
"""
|
||||
if "name" not in config:
|
||||
raise ValueError("Resource type config must include 'name'.")
|
||||
if "resource_kind" not in config:
|
||||
raise ValueError("Resource type config must include 'resource_kind'.")
|
||||
if "sandbox_strategy" not in config:
|
||||
raise ValueError("Resource type config must include 'sandbox_strategy'.")
|
||||
|
||||
# Resolve physical boolean from either new or legacy field
|
||||
if "physical" in config:
|
||||
physical = bool(config["physical"])
|
||||
elif "resource_kind" in config:
|
||||
# Legacy backward compatibility: convert resource_kind enum to bool
|
||||
rk = config["resource_kind"]
|
||||
if rk == "physical":
|
||||
physical = True
|
||||
elif rk == "virtual":
|
||||
physical = False
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid resource_kind '{rk}'. Must be 'physical' or 'virtual'."
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Resource type config must include 'physical' (boolean) field. "
|
||||
"Legacy 'resource_kind' is also accepted for backward compat."
|
||||
)
|
||||
|
||||
# Parse CLI arguments from list of mappings
|
||||
cli_args: list[ResourceTypeArgument] = []
|
||||
for arg_data in config.get("cli_args", []):
|
||||
@@ -384,7 +414,7 @@ class ResourceTypeSpec(BaseModel):
|
||||
return cls(
|
||||
name=config["name"],
|
||||
description=config.get("description", ""),
|
||||
resource_kind=ResourceKind(config["resource_kind"]),
|
||||
physical=physical,
|
||||
sandbox_strategy=SandboxStrategy(config["sandbox_strategy"]),
|
||||
user_addable=config.get("user_addable", True),
|
||||
cli_args=cli_args,
|
||||
@@ -401,7 +431,7 @@ class ResourceTypeSpec(BaseModel):
|
||||
"sandbox": False,
|
||||
"checkpoint": False,
|
||||
}
|
||||
if config.get("resource_kind") == "virtual"
|
||||
if not physical
|
||||
else {
|
||||
"read": True,
|
||||
"write": True,
|
||||
@@ -422,7 +452,7 @@ class ResourceTypeSpec(BaseModel):
|
||||
"""
|
||||
result: dict[str, Any] = {
|
||||
"name": self.name,
|
||||
"resource_kind": self.resource_kind.value,
|
||||
"physical": self.physical,
|
||||
"sandbox_strategy": self.sandbox_strategy.value,
|
||||
}
|
||||
|
||||
@@ -454,7 +484,7 @@ class ResourceTypeSpec(BaseModel):
|
||||
result["capabilities"] = dict(self.capabilities)
|
||||
|
||||
if self.auto_discovery is not None:
|
||||
# Deep copy — two schemas coexist: rules-based (list[dict])
|
||||
# Deep copy -- two schemas coexist: rules-based (list[dict])
|
||||
# and trigger-based (list[str]). copy.deepcopy handles both.
|
||||
import copy
|
||||
|
||||
|
||||
@@ -1817,9 +1817,7 @@ class ResourceTypeRepository:
|
||||
if "/" in resource_type.name
|
||||
else "builtin",
|
||||
description=resource_type.description or None,
|
||||
resource_kind=resource_type.resource_kind.value
|
||||
if hasattr(resource_type.resource_kind, "value")
|
||||
else resource_type.resource_kind,
|
||||
resource_kind="physical" if resource_type.physical else "virtual",
|
||||
sandbox_strategy=resource_type.sandbox_strategy.value
|
||||
if hasattr(resource_type.sandbox_strategy, "value")
|
||||
else resource_type.sandbox_strategy,
|
||||
@@ -1939,11 +1937,7 @@ class ResourceTypeRepository:
|
||||
raise ResourceTypeNotFoundError(resource_type.name)
|
||||
|
||||
row.description = resource_type.description or None # type: ignore[assignment]
|
||||
row.resource_kind = ( # type: ignore[assignment]
|
||||
resource_type.resource_kind.value
|
||||
if hasattr(resource_type.resource_kind, "value")
|
||||
else resource_type.resource_kind
|
||||
)
|
||||
row.resource_kind = "physical" if resource_type.physical else "virtual" # type: ignore[assignment]
|
||||
row.sandbox_strategy = ( # type: ignore[assignment]
|
||||
resource_type.sandbox_strategy.value
|
||||
if hasattr(resource_type.sandbox_strategy, "value")
|
||||
@@ -2048,7 +2042,6 @@ class ResourceTypeRepository:
|
||||
def _to_domain(row: ResourceTypeModel) -> Any:
|
||||
"""Convert a ``ResourceTypeModel`` row to a ``ResourceTypeSpec``."""
|
||||
from cleveragents.domain.models.core.resource_type import (
|
||||
ResourceKind,
|
||||
ResourceTypeArgument,
|
||||
ResourceTypeSpec,
|
||||
SandboxStrategy,
|
||||
@@ -2096,7 +2089,7 @@ class ResourceTypeRepository:
|
||||
return ResourceTypeSpec(
|
||||
name=name_str,
|
||||
description=cast(str, row.description or ""),
|
||||
resource_kind=ResourceKind(cast(str, row.resource_kind)),
|
||||
physical=cast(str, row.resource_kind) == "physical",
|
||||
sandbox_strategy=SandboxStrategy(cast(str, row.sandbox_strategy)),
|
||||
user_addable=cast(bool, row.user_addable),
|
||||
cli_args=cli_args,
|
||||
|
||||
@@ -63,7 +63,7 @@ logger = logging.getLogger(__name__)
|
||||
POSTGRES_TYPE_DEF: dict[str, Any] = {
|
||||
"name": "postgres",
|
||||
"description": "PostgreSQL database connection.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "transaction_rollback",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -134,7 +134,7 @@ POSTGRES_TYPE_DEF: dict[str, Any] = {
|
||||
MYSQL_TYPE_DEF: dict[str, Any] = {
|
||||
"name": "mysql",
|
||||
"description": "MySQL database connection.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "transaction_rollback",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -205,7 +205,7 @@ MYSQL_TYPE_DEF: dict[str, Any] = {
|
||||
SQLITE_TYPE_DEF: dict[str, Any] = {
|
||||
"name": "sqlite",
|
||||
"description": "SQLite database file.",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "transaction_rollback",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
@@ -233,7 +233,7 @@ SQLITE_TYPE_DEF: dict[str, Any] = {
|
||||
DUCKDB_TYPE_DEF: dict[str, Any] = {
|
||||
"name": "duckdb",
|
||||
"description": "DuckDB database (file-based or in-memory).",
|
||||
"resource_kind": "physical",
|
||||
"physical": True,
|
||||
"sandbox_strategy": "transaction_rollback",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
|
||||
@@ -10,8 +10,8 @@ Provides :class:`ResourceTypeConfigSchema`, a Pydantic model that:
|
||||
Schema definition lives in ``docs/schema/resource_type.schema.yaml``.
|
||||
|
||||
Based on:
|
||||
- docs/specification.md — Resource Types, Resource Registry
|
||||
- implementation_plan.md — Task B0.type-model
|
||||
- docs/specification.md -- Resource Types, Resource Registry
|
||||
- implementation_plan.md -- Task B0.type-model
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -43,9 +43,6 @@ _ENV_VAR_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
|
||||
#: Supported schema versions.
|
||||
_SUPPORTED_VERSIONS = frozenset({"1", "1.0"})
|
||||
|
||||
#: Valid resource kinds.
|
||||
_VALID_KINDS = frozenset({"physical", "virtual"})
|
||||
|
||||
#: Valid sandbox strategies.
|
||||
_VALID_STRATEGIES = frozenset(
|
||||
{
|
||||
@@ -147,8 +144,8 @@ class ResourceTypeConfigSchema(BaseModel):
|
||||
|
||||
Create instances via the factory class methods:
|
||||
|
||||
- :meth:`from_yaml` — parse a YAML string
|
||||
- :meth:`from_yaml_file` — parse a YAML file from disk
|
||||
- :meth:`from_yaml` -- parse a YAML string
|
||||
- :meth:`from_yaml_file` -- parse a YAML file from disk
|
||||
"""
|
||||
|
||||
schema_version: str = Field(
|
||||
@@ -163,9 +160,20 @@ class ResourceTypeConfigSchema(BaseModel):
|
||||
"",
|
||||
description="Short description of the resource type.",
|
||||
)
|
||||
resource_kind: str = Field(
|
||||
...,
|
||||
description="Classification: physical | virtual.",
|
||||
physical: bool | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"true for physical types (concrete manifestations), "
|
||||
"false for virtual types (abstract identity linking equivalent "
|
||||
"physical resources). Per spec JSON Schema line 34580."
|
||||
),
|
||||
)
|
||||
resource_kind: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Deprecated: use physical boolean instead. "
|
||||
"Classification: physical | virtual."
|
||||
),
|
||||
)
|
||||
sandbox_strategy: str = Field(
|
||||
...,
|
||||
@@ -281,17 +289,6 @@ class ResourceTypeConfigSchema(BaseModel):
|
||||
)
|
||||
return v
|
||||
|
||||
@field_validator("resource_kind")
|
||||
@classmethod
|
||||
def validate_resource_kind(cls, v: str) -> str:
|
||||
"""Validate resource kind is physical or virtual."""
|
||||
v_lower = v.lower()
|
||||
if v_lower not in _VALID_KINDS:
|
||||
raise ValueError(
|
||||
f"Invalid resource_kind '{v}'. Must be 'physical' or 'virtual'."
|
||||
)
|
||||
return v_lower
|
||||
|
||||
@field_validator("sandbox_strategy")
|
||||
@classmethod
|
||||
def validate_sandbox_strategy(cls, v: str) -> str:
|
||||
@@ -311,6 +308,25 @@ class ResourceTypeConfigSchema(BaseModel):
|
||||
@model_validator(mode="after")
|
||||
def validate_cross_fields(self) -> ResourceTypeConfigSchema:
|
||||
"""Cross-field validation for resource type config."""
|
||||
# Resolve physical from resource_kind for backward compatibility
|
||||
if self.physical is None:
|
||||
if self.resource_kind is not None:
|
||||
rk = self.resource_kind.lower()
|
||||
if rk == "physical":
|
||||
object.__setattr__(self, "physical", True)
|
||||
elif rk == "virtual":
|
||||
object.__setattr__(self, "physical", False)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid resource_kind '{self.resource_kind}'. "
|
||||
"Must be 'physical' or 'virtual'."
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Resource type config must include 'physical' (boolean) "
|
||||
"or legacy 'resource_kind' field."
|
||||
)
|
||||
|
||||
# Custom types must be namespaced
|
||||
if not self.built_in and "/" not in self.name:
|
||||
raise ValueError(
|
||||
@@ -320,7 +336,7 @@ class ResourceTypeConfigSchema(BaseModel):
|
||||
)
|
||||
|
||||
# Virtual types require equivalence
|
||||
if self.resource_kind == "virtual" and self.equivalence is None:
|
||||
if not self.physical and self.equivalence is None:
|
||||
raise ValueError(
|
||||
f"Virtual resource type '{self.name}' requires an "
|
||||
"'equivalence' configuration."
|
||||
|
||||
Reference in New Issue
Block a user