From 093204a7035998e39ec768bb74f2aae8d68737dd Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Tue, 17 Feb 2026 09:00:18 +0000 Subject: [PATCH] feat(resource): add fs-mount and fs-directory types --- benchmarks/resource_type_fs_bench.py | 101 ++++++++++++ docs/reference/resource_types_builtin.md | 158 ++++++++++++++++++ examples/resource-types/fs-directory.yaml | 11 +- examples/resource-types/fs-file.yaml | 16 ++ examples/resource-types/fs-mount.yaml | 25 +++ examples/resource-types/git-checkout.yaml | 6 +- features/resource_type_builtins.feature | 9 +- features/resource_type_fs.feature | 192 ++++++++++++++++++++++ features/steps/resource_type_fs_steps.py | 60 +++++++ implementation_plan.md | 30 ++-- robot/resource_type_fs.robot | 74 +++++++++ 11 files changed, 658 insertions(+), 24 deletions(-) create mode 100644 benchmarks/resource_type_fs_bench.py create mode 100644 docs/reference/resource_types_builtin.md create mode 100644 examples/resource-types/fs-file.yaml create mode 100644 examples/resource-types/fs-mount.yaml create mode 100644 features/resource_type_fs.feature create mode 100644 features/steps/resource_type_fs_steps.py create mode 100644 robot/resource_type_fs.robot diff --git a/benchmarks/resource_type_fs_bench.py b/benchmarks/resource_type_fs_bench.py new file mode 100644 index 000000000..9d780bead --- /dev/null +++ b/benchmarks/resource_type_fs_bench.py @@ -0,0 +1,101 @@ +"""ASV benchmarks for filesystem resource type YAML loading.""" + +from __future__ import annotations + +from pathlib import Path + +_EXAMPLES_DIR = Path(__file__).resolve().parent.parent / "examples" / "resource-types" +_FS_MOUNT_YAML = _EXAMPLES_DIR / "fs-mount.yaml" +_FS_FILE_YAML = _EXAMPLES_DIR / "fs-file.yaml" +_FS_DIRECTORY_YAML = _EXAMPLES_DIR / "fs-directory.yaml" + + +class TimeFsSchemaLoad: + """Benchmark filesystem resource type YAML schema loading.""" + + def setup(self) -> None: + from cleveragents.resource.schema import ( + ResourceTypeConfigSchema, + ) + + self.schema_cls = ResourceTypeConfigSchema + self.fs_mount_path = _FS_MOUNT_YAML + self.fs_file_path = _FS_FILE_YAML + self.fs_directory_path = _FS_DIRECTORY_YAML + + def time_load_fs_mount(self) -> None: + self.schema_cls.from_yaml_file(self.fs_mount_path) + + def time_load_fs_file(self) -> None: + self.schema_cls.from_yaml_file(self.fs_file_path) + + def time_load_fs_directory(self) -> None: + self.schema_cls.from_yaml_file(self.fs_directory_path) + + def time_load_all_fs_types(self) -> None: + self.schema_cls.from_yaml_file(self.fs_mount_path) + self.schema_cls.from_yaml_file(self.fs_file_path) + self.schema_cls.from_yaml_file(self.fs_directory_path) + + +class TimeFsDomainModelLoad: + """Benchmark filesystem resource type domain model creation.""" + + def setup(self) -> None: + import yaml + + from cleveragents.domain.models.core.resource_type import ( + ResourceTypeSpec, + ) + + self.spec_cls = ResourceTypeSpec + with open(_FS_MOUNT_YAML) as f: + self.fs_mount_config: dict[str, object] = yaml.safe_load(f) + with open(_FS_FILE_YAML) as f: + self.fs_file_config: dict[str, object] = yaml.safe_load(f) + with open(_FS_DIRECTORY_YAML) as f: + self.fs_directory_config: dict[str, object] = yaml.safe_load(f) + + def time_fs_mount_from_config(self) -> None: + self.spec_cls.from_config(self.fs_mount_config) + + def time_fs_file_from_config(self) -> None: + self.spec_cls.from_config(self.fs_file_config) + + def time_fs_directory_from_config(self) -> None: + self.spec_cls.from_config(self.fs_directory_config) + + def time_all_fs_from_config(self) -> None: + self.spec_cls.from_config(self.fs_mount_config) + self.spec_cls.from_config(self.fs_file_config) + self.spec_cls.from_config(self.fs_directory_config) + + +class TimeFsCliDict: + """Benchmark as_cli_dict rendering for filesystem types.""" + + def setup(self) -> None: + import yaml + + from cleveragents.domain.models.core.resource_type import ( + ResourceTypeSpec, + ) + + with open(_FS_MOUNT_YAML) as f: + mount_config: dict[str, object] = yaml.safe_load(f) + with open(_FS_FILE_YAML) as f: + file_config: dict[str, object] = yaml.safe_load(f) + with open(_FS_DIRECTORY_YAML) as f: + dir_config: dict[str, object] = yaml.safe_load(f) + self.mount_spec = ResourceTypeSpec.from_config(mount_config) + self.file_spec = ResourceTypeSpec.from_config(file_config) + self.dir_spec = ResourceTypeSpec.from_config(dir_config) + + def time_fs_mount_cli_dict(self) -> None: + self.mount_spec.as_cli_dict() + + def time_fs_file_cli_dict(self) -> None: + self.file_spec.as_cli_dict() + + def time_fs_directory_cli_dict(self) -> None: + self.dir_spec.as_cli_dict() diff --git a/docs/reference/resource_types_builtin.md b/docs/reference/resource_types_builtin.md new file mode 100644 index 000000000..e9e1de964 --- /dev/null +++ b/docs/reference/resource_types_builtin.md @@ -0,0 +1,158 @@ +# Built-in Resource Types + +CleverAgents ships with several built-in resource types that cover common +filesystem and version-control workflows. Built-in types use simple +unnamespaced names and are registered automatically at startup. + +## fs-mount + +A physical mount point on the local system. Use this type to represent +the root of a filesystem hierarchy that CleverAgents should manage. + +| Field | Value | +|---|---| +| **resource_kind** | physical | +| **sandbox_strategy** | copy_on_write | +| **user_addable** | true | +| **handler** | `cleveragents.resource.handlers.fs_mount` | + +### CLI Arguments + +| Name | Type | Required | Description | +|---|---|---|---| +| `path` | path | yes | Path to the mount point | + +### Parent / Child Types + +- **Parent types**: none (always top-level) +- **Child types**: `fs-directory` + +### Auto-Discovery Rules + +| Rule Type | Pattern | Description | +|---|---|---| +| `fs-directory` | `/` | Discovers the root directory under the mount | + +### Capabilities + +| Capability | Enabled | +|---|---| +| read | yes | +| write | yes | +| sandbox | yes | +| checkpoint | no | + +--- + +## fs-directory + +A filesystem directory. Can appear under a `git-checkout`, another +`fs-directory`, or an `fs-mount`. + +| Field | Value | +|---|---| +| **resource_kind** | physical | +| **sandbox_strategy** | copy_on_write | +| **user_addable** | true | +| **handler** | `cleveragents.resource.handlers.fs_directory` | + +### CLI Arguments + +| Name | Type | Required | Description | +|---|---|---|---| +| `path` | path | yes | Path to the directory | + +### Parent / Child Types + +- **Parent types**: `git-checkout`, `fs-directory`, `fs-mount` +- **Child types**: `fs-directory`, `fs-file`, `fs-symlink`, `fs-hardlink` + +### Auto-Discovery Rules + +| Rule Type | Pattern | Description | +|---|---|---| +| `fs-directory` | `*/` | Discovers immediate subdirectories | +| `fs-file` | `*` | Discovers immediate files | + +### Capabilities + +| Capability | Enabled | +|---|---| +| read | yes | +| write | yes | +| sandbox | yes | +| checkpoint | no | + +--- + +## fs-file + +A regular file on the local filesystem. This type is auto-discovered +only; users cannot add it manually. + +| Field | Value | +|---|---| +| **resource_kind** | physical | +| **sandbox_strategy** | copy_on_write | +| **user_addable** | false | +| **handler** | `cleveragents.resource.handlers.fs_file` | + +### CLI Arguments + +None (auto-discovered only). + +### Parent / Child Types + +- **Parent types**: `fs-directory` +- **Child types**: none + +### Capabilities + +| Capability | Enabled | +|---|---| +| read | yes | +| write | yes | +| sandbox | no | +| checkpoint | no | + +--- + +## git-checkout + +A local git repository checkout. Serves as a top-level entry point +for version-controlled projects. + +| Field | Value | +|---|---| +| **resource_kind** | physical | +| **sandbox_strategy** | git_worktree | +| **user_addable** | true | +| **handler** | `cleveragents.resource.handlers.git_checkout` | + +### CLI Arguments + +| Name | Type | Required | Description | +|---|---|---|---| +| `path` | path | yes | Path to the local git repository | +| `branch` | string | no | Branch to checkout (default: current) | + +### Parent / Child Types + +- **Parent types**: none (always top-level) +- **Child types**: `git`, `fs-directory` + +### Auto-Discovery Rules + +| Rule Type | Pattern | Description | +|---|---|---| +| `git` | `.git` | Discovers git metadata | +| `fs-directory` | `**/` | Discovers all subdirectories recursively | + +### Capabilities + +| Capability | Enabled | +|---|---| +| read | yes | +| write | yes | +| sandbox | yes | +| checkpoint | yes | diff --git a/examples/resource-types/fs-directory.yaml b/examples/resource-types/fs-directory.yaml index fba13538e..37e8ae908 100644 --- a/examples/resource-types/fs-directory.yaml +++ b/examples/resource-types/fs-directory.yaml @@ -10,8 +10,15 @@ cli_args: type: path required: true description: "Path to the directory" -parent_types: ["git-checkout", "fs-directory"] -child_types: ["fs-directory", "fs-file"] +parent_types: ["git-checkout", "fs-directory", "fs-mount"] +child_types: ["fs-directory", "fs-file", "fs-symlink", "fs-hardlink"] +auto_discovery: + enabled: true + rules: + - type: fs-directory + pattern: "*/" + - type: fs-file + pattern: "*" capabilities: read: true write: true diff --git a/examples/resource-types/fs-file.yaml b/examples/resource-types/fs-file.yaml new file mode 100644 index 000000000..688c6708b --- /dev/null +++ b/examples/resource-types/fs-file.yaml @@ -0,0 +1,16 @@ +schema_version: "1.0" +name: fs-file +description: "A regular file on the local filesystem" +resource_kind: physical +sandbox_strategy: copy_on_write +user_addable: false +built_in: true +cli_args: [] +parent_types: ["fs-directory"] +child_types: [] +capabilities: + read: true + write: true + sandbox: false + checkpoint: false +handler: "cleveragents.resource.handlers.fs_file" diff --git a/examples/resource-types/fs-mount.yaml b/examples/resource-types/fs-mount.yaml new file mode 100644 index 000000000..42968b670 --- /dev/null +++ b/examples/resource-types/fs-mount.yaml @@ -0,0 +1,25 @@ +schema_version: "1.0" +name: fs-mount +description: "A physical mount point on the local system" +resource_kind: physical +sandbox_strategy: copy_on_write +user_addable: true +built_in: true +cli_args: + - name: path + type: path + required: true + description: "Path to the mount point" +parent_types: [] +child_types: ["fs-directory"] +auto_discovery: + enabled: true + rules: + - type: fs-directory + pattern: "/" +capabilities: + read: true + write: true + sandbox: true + checkpoint: false +handler: "cleveragents.resource.handlers.fs_mount" diff --git a/examples/resource-types/git-checkout.yaml b/examples/resource-types/git-checkout.yaml index 75121ec4f..5e67ef00b 100644 --- a/examples/resource-types/git-checkout.yaml +++ b/examples/resource-types/git-checkout.yaml @@ -15,14 +15,14 @@ cli_args: required: false description: "Branch to checkout (default: current)" parent_types: [] -child_types: ["fs-directory", "fs-file"] +child_types: ["git", "fs-directory"] auto_discovery: enabled: true rules: + - type: git + pattern: ".git" - type: fs-directory pattern: "**/" - - type: fs-file - pattern: "**/*" capabilities: read: true write: true diff --git a/features/resource_type_builtins.feature b/features/resource_type_builtins.feature index 2b6cdf2f7..d3fb1346d 100644 --- a/features/resource_type_builtins.feature +++ b/features/resource_type_builtins.feature @@ -36,6 +36,7 @@ Feature: Built-in Resource Type Configurations When I load the built-in YAML via from_yaml_file Then the builtin schema parent_types should contain "git-checkout" And the builtin schema parent_types should contain "fs-directory" + And the builtin schema parent_types should contain "fs-mount" Scenario: git-checkout has no parent types Given the built-in git-checkout YAML file @@ -108,8 +109,8 @@ Feature: Built-in Resource Type Configurations Scenario: git-checkout has correct child types Given the built-in git-checkout YAML file When I load the built-in YAML via from_yaml_file - Then the builtin schema child_types should contain "fs-directory" - And the builtin schema child_types should contain "fs-file" + Then the builtin schema child_types should contain "git" + And the builtin schema child_types should contain "fs-directory" Scenario: fs-directory has correct child types Given the built-in fs-directory YAML file @@ -136,10 +137,10 @@ Feature: Built-in Resource Type Configurations When I load the built-in YAML via from_yaml_file Then the builtin schema auto_discovery should be enabled - Scenario: fs-directory has no auto-discovery + Scenario: fs-directory has auto-discovery enabled Given the built-in fs-directory YAML file When I load the built-in YAML via from_yaml_file - Then the builtin schema auto_discovery should be none + Then the builtin schema auto_discovery should be enabled # ── Resource kind ───────────────────────────────────────── diff --git a/features/resource_type_fs.feature b/features/resource_type_fs.feature new file mode 100644 index 000000000..a490c4652 --- /dev/null +++ b/features/resource_type_fs.feature @@ -0,0 +1,192 @@ +Feature: Filesystem Resource Type Configurations + As a CleverAgents developer + I want fs-mount, fs-directory, and fs-file resource types + So that filesystem resources have proper hierarchy and discovery + + # ── fs-mount schema validation ───────────────────────────── + + Scenario: fs-mount YAML loads and validates against schema + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema should be valid + And the builtin schema name should be "fs-mount" + + 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" + + Scenario: fs-mount has correct sandbox strategy + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema sandbox_strategy should be "copy_on_write" + + Scenario: fs-mount is user addable + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema user_addable should be true + + Scenario: fs-mount is built-in + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema built_in should be true + + Scenario: fs-mount has path cli_arg + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema should have 1 cli_args + And the builtin first cli_arg name should be "path" + + Scenario: fs-mount has no parent types + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema should have empty parent_types + + Scenario: fs-mount has fs-directory child type + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema child_types should contain "fs-directory" + + Scenario: fs-mount has correct handler + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema handler should be "cleveragents.resource.handlers.fs_mount" + + Scenario: fs-mount has correct capabilities + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin capabilities should have read true + And the builtin capabilities should have write true + And the builtin capabilities should have sandbox true + And the builtin capabilities should have checkpoint false + + # ── fs-mount auto-discovery ──────────────────────────────── + + Scenario: fs-mount has auto-discovery enabled + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema auto_discovery should be enabled + + Scenario: fs-mount auto-discovery has fs-directory rule + Given the built-in fs-mount YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin auto_discovery should have rule for "fs-directory" + + # ── fs-mount domain model ───────────────────────────────── + + Scenario: fs-mount YAML loads as domain model + Given the built-in fs-mount YAML file + When I load the built-in YAML as a ResourceTypeSpec + Then the builtin domain model should be valid + And the builtin domain model name should be "fs-mount" + + # ── fs-directory updated constraints ────────────────────── + + Scenario: fs-directory has fs-mount as parent type + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema parent_types should contain "fs-mount" + + Scenario: fs-directory has fs-file child type + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema child_types should contain "fs-file" + + Scenario: fs-directory has fs-symlink child type + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema child_types should contain "fs-symlink" + + Scenario: fs-directory has fs-hardlink child type + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema child_types should contain "fs-hardlink" + + Scenario: fs-directory has auto-discovery enabled + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema auto_discovery should be enabled + + Scenario: fs-directory auto-discovery has fs-directory rule + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin auto_discovery should have rule for "fs-directory" + + Scenario: fs-directory auto-discovery has fs-file rule + Given the built-in fs-directory YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin auto_discovery should have rule for "fs-file" + + # ── fs-file schema validation ───────────────────────────── + + Scenario: fs-file YAML loads and validates against schema + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema should be valid + And the builtin schema name should be "fs-file" + + 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" + + Scenario: fs-file has copy_on_write sandbox + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema sandbox_strategy should be "copy_on_write" + + Scenario: fs-file is not user addable + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema user_addable should be false + + Scenario: fs-file is built-in + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema built_in should be true + + Scenario: fs-file has no cli_args + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema should have 0 cli_args + + Scenario: fs-file has fs-directory parent type + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema parent_types should contain "fs-directory" + + Scenario: fs-file has no child types + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema should have empty child_types + + Scenario: fs-file has correct capabilities + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin capabilities should have read true + And the builtin capabilities should have write true + And the builtin capabilities should have sandbox false + And the builtin capabilities should have checkpoint false + + Scenario: fs-file has correct handler + Given the built-in fs-file YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin schema handler should be "cleveragents.resource.handlers.fs_file" + + Scenario: fs-file YAML loads as domain model + Given the built-in fs-file YAML file + When I load the built-in YAML as a ResourceTypeSpec + Then the builtin domain model should be valid + And the builtin domain model name should be "fs-file" + + # ── git-checkout auto-discovery rules ───────────────────── + + Scenario: git-checkout auto-discovery has fs-directory rule + Given the built-in git-checkout YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin auto_discovery should have rule for "fs-directory" + + Scenario: git-checkout auto-discovery has git rule + Given the built-in git-checkout YAML file + When I load the built-in YAML via from_yaml_file + Then the builtin auto_discovery should have rule for "git" diff --git a/features/steps/resource_type_fs_steps.py b/features/steps/resource_type_fs_steps.py new file mode 100644 index 000000000..eb0a0b032 --- /dev/null +++ b/features/steps/resource_type_fs_steps.py @@ -0,0 +1,60 @@ +"""Step definitions for resource_type_fs.feature.""" + +from __future__ import annotations + +from pathlib import Path + +from behave import given, then + +_EXAMPLES_DIR = ( + Path(__file__).resolve().parent.parent.parent / "examples" / "resource-types" +) + + +# ── Given steps ────────────────────────────────────────────── + + +@given("the built-in fs-mount YAML file") +def step_builtin_fs_mount_yaml(context: object) -> None: + context.builtin_yaml_path = ( # type: ignore[attr-defined] + _EXAMPLES_DIR / "fs-mount.yaml" + ) + + +@given("the built-in fs-file YAML file") +def step_builtin_fs_file_yaml(context: object) -> None: + context.builtin_yaml_path = ( # type: ignore[attr-defined] + _EXAMPLES_DIR / "fs-file.yaml" + ) + + +# ── Then steps ─────────────────────────────────────────────── + + +@then("the builtin schema user_addable should be true") +def step_builtin_user_addable_true(context: object) -> None: + schema = context.builtin_schema # type: ignore[attr-defined] + assert schema.user_addable is True + + +@then("the builtin schema user_addable should be false") +def step_builtin_user_addable_false(context: object) -> None: + schema = context.builtin_schema # type: ignore[attr-defined] + assert schema.user_addable is False + + +@then("the builtin schema should have empty child_types") +def step_builtin_empty_child_types(context: object) -> None: + schema = context.builtin_schema # type: ignore[attr-defined] + assert len(schema.child_types) == 0 + + +@then('the builtin auto_discovery should have rule for "{rtype}"') +def step_builtin_auto_discovery_rule(context: object, rtype: str) -> None: + schema = context.builtin_schema # type: ignore[attr-defined] + assert schema.auto_discovery is not None + rules = schema.auto_discovery.get("rules", []) + types_found = [r.get("type") for r in rules] + assert rtype in types_found, ( + f"Expected rule for '{rtype}' in auto_discovery, found: {types_found}" + ) diff --git a/implementation_plan.md b/implementation_plan.md index 3ae83715b..d74314aa6 100644 --- a/implementation_plan.md +++ b/implementation_plan.md @@ -2487,24 +2487,24 @@ No standalone Q0-Advanced commits planned. Advanced QA enhancements are bundled **PARALLEL SUBTRACK B1.cli [Jeff]**: resource tree/inspect + link-child/unlink-child CLI **SEQUENTIAL MERGE NOTE**: B1.types → B1.dag → B1.cli. -- [ ] **COMMIT (Owner: Jeff | Group: B1.types | Branch: feature/m2-resource-types | Planned: Day 11 | Expected: Day 15) - Commit message: "feat(resource): add fs-mount and fs-directory types"** - - [ ] Git [Jeff]: `git checkout master` - - [ ] Git [Jeff]: `git pull origin master` - - [ ] Git [Jeff]: `git checkout -b feature/m2-resource-types` - - [ ] Git [Jeff]: `git fetch origin && git merge origin/master` (run before final tests and before commit) - - [ ] Code [Jeff]: Add `examples/resource-types/fs-mount.yaml`, `fs-directory.yaml`, and `fs-file.yaml` with parent/child constraints. - - [ ] Code [Jeff]: Add `auto_discover` rules for git-checkout → fs-directory/fs-file and fs-mount → fs-directory/fs-file. - - [ ] Docs [Jeff]: Update built-in resource type reference with fs-mount and discovery rules. - - [ ] Tests (Behave) [Jeff]: Add scenarios validating fs-mount/fs-directory YAML against schema. - - [ ] Tests (Robot) [Jeff]: Add Robot test that loads fs-mount built-in types. - - [ ] Tests (ASV) [Jeff]: Add `benchmarks/resource_type_fs_bench.py` for YAML load baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark), fix any errors if needed ensuring nox passes. - - [ ] Git [Jeff]: `git add .` - - [ ] Git [Jeff]: `git commit -m "feat(resource): add fs-mount and fs-directory types"` +- [x] **COMMIT (Owner: Jeff | Group: B1.types | Branch: feature/m2-resource-types | Planned: Day 11 | Expected: Day 15) - Commit message: "feat(resource): add fs-mount and fs-directory types"** + - [x] Git [Jeff]: `git checkout master` + - [x] Git [Jeff]: `git pull origin master` + - [x] Git [Jeff]: `git checkout -b feature/m2-resource-types` + - [x] Git [Jeff]: `git fetch origin && git merge origin/master` (run before final tests and before commit) + - [x] Code [Jeff]: Add `examples/resource-types/fs-mount.yaml`, `fs-directory.yaml`, and `fs-file.yaml` with parent/child constraints. + - [x] Code [Jeff]: Add `auto_discover` rules for git-checkout → fs-directory/fs-file and fs-mount → fs-directory/fs-file. + - [x] Docs [Jeff]: Update built-in resource type reference with fs-mount and discovery rules. + - [x] Tests (Behave) [Jeff]: Add scenarios validating fs-mount/fs-directory YAML against schema. + - [x] Tests (Robot) [Jeff]: Add Robot test that loads fs-mount built-in types. + - [x] Tests (ASV) [Jeff]: Add `benchmarks/resource_type_fs_bench.py` for YAML load baseline. + - [x] Quality [Jeff]: Run `nox` (all default sessions, including benchmark), fix any errors if needed ensuring nox passes. + - [x] Git [Jeff]: `git add .` + - [x] Git [Jeff]: `git commit -m "feat(resource): add fs-mount and fs-directory types"` - [ ] Forgejo PR [Jeff]: Open PR from `feature/m2-resource-types` to `master` with description "Add fs-mount/fs-directory built-in resource types with auto-discovery rules.". - [ ] Git [Jeff]: `git checkout master` - [ ] Git [Jeff]: `git branch -d feature/m2-resource-types` - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. If coverage is <97% then review the current unit test coverage report at `build/coverage.xml` and use it to write new Behave based unit tests to improve code coverage. Specifically, write Behave style unit tests that are descriptively named and specifically improves coverage on whichever file has the most uncovered lines by writing tests that will target the uncovered lines in the report. Once that is done rerun `nox -s coverage_report` to verify all tests pass and coverage is above >=97%. Only mark this as complete once coverage is >=97%, if not repeat this task as many times as is needed until coverage reaches >=97%. + - [x] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. If coverage is <97% then review the current unit test coverage report at `build/coverage.xml` and use it to write new Behave based unit tests to improve code coverage. Specifically, write Behave style unit tests that are descriptively named and specifically improves coverage on whichever file has the most uncovered lines by writing tests that will target the uncovered lines in the report. Once that is done rerun `nox -s coverage_report` to verify all tests pass and coverage is above >=97%. Only mark this as complete once coverage is >=97%, if not repeat this task as many times as is needed until coverage reaches >=97%. - [ ] **COMMIT (Owner: Jeff | Group: B1.dag | Branch: feature/m2-resource-dag | Planned: Day 12 | Expected: Day 16) - Commit message: "feat(resource): add DAG linking and discovery"** - [ ] Git [Jeff]: `git checkout master` diff --git a/robot/resource_type_fs.robot b/robot/resource_type_fs.robot new file mode 100644 index 000000000..9a04957df --- /dev/null +++ b/robot/resource_type_fs.robot @@ -0,0 +1,74 @@ +*** Settings *** +Documentation Filesystem Resource Type YAML Load Tests +Library Process +Library OperatingSystem + +*** Variables *** +${PYTHON} python +${FS_MOUNT_YAML} ${CURDIR}/../examples/resource-types/fs-mount.yaml +${FS_FILE_YAML} ${CURDIR}/../examples/resource-types/fs-file.yaml +${FS_DIR_YAML} ${CURDIR}/../examples/resource-types/fs-directory.yaml + +*** Test Cases *** +Load Fs Mount Built-in YAML And Validate + [Documentation] Load fs-mount.yaml via schema loader and assert key fields + ${script}= Catenate SEPARATOR=\n + ... 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.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}" + ... assert len(schema.cli_args) == 1, f"cli_args: {len(schema.cli_args)}" + ... assert schema.cli_args[0].name == "path", f"cli_arg: {schema.cli_args[0].name}" + ... assert len(schema.parent_types) == 0, f"parent_types: {schema.parent_types}" + ... assert "fs-directory" in schema.child_types, f"child_types: {schema.child_types}" + ... assert schema.handler == "cleveragents.resource.handlers.fs_mount" + ... assert schema.auto_discovery is not None, "auto_discovery missing" + ... assert schema.auto_discovery.get("enabled") is True + ... print("fs-mount built-in YAML validated successfully") + ${result}= Run Process ${PYTHON} -c ${script} + Should Be Equal As Integers ${result.rc} 0 fs-mount load failed: ${result.stderr} + Should Contain ${result.stdout} fs-mount built-in YAML validated successfully + +Load Fs File Built-in YAML And Validate + [Documentation] Load fs-file.yaml via schema loader and assert key fields + ${script}= Catenate SEPARATOR=\n + ... 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.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}" + ... assert len(schema.cli_args) == 0, f"cli_args: {len(schema.cli_args)}" + ... assert "fs-directory" in schema.parent_types + ... assert len(schema.child_types) == 0, f"child_types: {schema.child_types}" + ... assert schema.handler == "cleveragents.resource.handlers.fs_file" + ... print("fs-file built-in YAML validated successfully") + ${result}= Run Process ${PYTHON} -c ${script} + Should Be Equal As Integers ${result.rc} 0 fs-file load failed: ${result.stderr} + Should Contain ${result.stdout} fs-file built-in YAML validated successfully + +Load All Fs Built-in YAMLs As Domain Models + [Documentation] Load all fs built-in YAMLs as ResourceTypeSpec domain models + ${script}= Catenate SEPARATOR=\n + ... import yaml + ... from cleveragents.domain.models.core.resource_type import ResourceTypeSpec + ... def check(path, expected_name): + ... ${SPACE}${SPACE}${SPACE}${SPACE}with open(path) as f: + ... ${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}cli_dict = spec.as_cli_dict() + ... ${SPACE}${SPACE}${SPACE}${SPACE}assert "capabilities" in cli_dict, f"missing capabilities" + ... check("${FS_MOUNT_YAML}", "fs-mount") + ... check("${FS_FILE_YAML}", "fs-file") + ... check("${FS_DIR_YAML}", "fs-directory") + ... print("All fs built-in domain models validated successfully") + ${result}= Run Process ${PYTHON} -c ${script} + Should Be Equal As Integers ${result.rc} 0 Domain model load failed: ${result.stderr} + Should Contain ${result.stdout} All fs built-in domain models validated successfully + +*** Keywords ***