fix(resources): register fs-mount built-in resource type at startup
CI / lint (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 49s
CI / quality (pull_request) Successful in 48s
CI / security (pull_request) Successful in 56s
CI / build (pull_request) Successful in 17s
CI / helm (pull_request) Successful in 25s
CI / unit_tests (pull_request) Successful in 6m44s
CI / e2e_tests (pull_request) Successful in 19m27s
CI / integration_tests (pull_request) Successful in 22m41s
CI / coverage (pull_request) Successful in 11m4s
CI / docker (pull_request) Successful in 1m24s
CI / status-check (pull_request) Successful in 2s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 57m3s
CI / lint (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 49s
CI / quality (pull_request) Successful in 48s
CI / security (pull_request) Successful in 56s
CI / build (pull_request) Successful in 17s
CI / helm (pull_request) Successful in 25s
CI / unit_tests (pull_request) Successful in 6m44s
CI / e2e_tests (pull_request) Successful in 19m27s
CI / integration_tests (pull_request) Successful in 22m41s
CI / coverage (pull_request) Successful in 11m4s
CI / docker (pull_request) Successful in 1m24s
CI / status-check (pull_request) Successful in 2s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 57m3s
Add fs-mount ResourceTypeDefinition to _GIT_FS_CONTAINER_TYPES in _resource_registry_data.py so that bootstrap_builtin_types() seeds it into the registry at startup. The fs-mount type was listed in BUILTIN_TYPE_NAMES (validation frozenset) but had no corresponding type definition in BUILTIN_TYPES, causing: - 'agents resource type list' to omit fs-mount - 'agents resource add fs-mount ...' to fail with 'Resource type not found' - fs-directory to be unable to reference fs-mount as a parent type Changes: - Add fs-mount definition to _GIT_FS_CONTAINER_TYPES with correct fields: name, description, resource_kind=physical, sandbox_strategy=copy_on_write, user_addable=True, cli_args=[path], parent_types=[], child_types=[fs-directory], auto_discovery with fs-directory rule, handler=fs_mount:FsMountHandler, capabilities (read/write/sandbox=True, checkpoint=False) - Add fs-mount to fs-directory's parent_types so fs-directory can be nested under an fs-mount resource - Add BDD scenarios in resource_type_bootstrap_fs_mount.feature covering registration, user_addable, child_types, parent_types, resource add, and fs-directory parent resolution - Add Robot Framework integration tests in resource_type_bootstrap_fs_mount.robot covering all four acceptance criteria end-to-end ISSUES CLOSED: #2911
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
Feature: Built-in fs-mount Resource Type Bootstrap
|
||||
As a CleverAgents user
|
||||
I want the built-in fs-mount resource type to be available after initialization
|
||||
So that I can run "agents resource add fs-mount" without "Resource type not found"
|
||||
|
||||
# ── Bug reproduction: fs-mount must be in BUILTIN_TYPES ───────────
|
||||
|
||||
@tdd_issue @tdd_issue_2911
|
||||
Scenario: fs-mount type exists after bootstrap
|
||||
Given a fresh in-memory resource registry with bootstrap
|
||||
When I query the fs bootstrap resource type registry for "fs-mount"
|
||||
Then the bootstrap fs resource type "fs-mount" should exist
|
||||
And the bootstrap fs resource type kind should be "physical"
|
||||
And the bootstrap fs resource type sandbox_strategy should be "copy_on_write"
|
||||
|
||||
@tdd_issue @tdd_issue_2911
|
||||
Scenario: fs-mount is user-addable after bootstrap
|
||||
Given a fresh in-memory resource registry with bootstrap
|
||||
When I query the fs bootstrap resource type registry for "fs-mount"
|
||||
Then the bootstrap fs resource type "fs-mount" should exist
|
||||
And the bootstrap fs resource type user_addable should be true
|
||||
|
||||
@tdd_issue @tdd_issue_2911
|
||||
Scenario: fs-mount has fs-directory as child type after bootstrap
|
||||
Given a fresh in-memory resource registry with bootstrap
|
||||
When I query the fs bootstrap resource type registry for "fs-mount"
|
||||
Then the bootstrap fs resource type "fs-mount" should exist
|
||||
And the bootstrap fs resource type child_types should contain "fs-directory"
|
||||
|
||||
@tdd_issue @tdd_issue_2911
|
||||
Scenario: fs-mount has no parent types after bootstrap
|
||||
Given a fresh in-memory resource registry with bootstrap
|
||||
When I query the fs bootstrap resource type registry for "fs-mount"
|
||||
Then the bootstrap fs resource type "fs-mount" should exist
|
||||
And the bootstrap fs resource type parent_types should be empty
|
||||
|
||||
@tdd_issue @tdd_issue_2911
|
||||
Scenario: resource add fs-mount succeeds after bootstrap
|
||||
Given a fresh in-memory resource registry with bootstrap
|
||||
When I run resource add for type "fs-mount" named "local/test-mount" with path "/tmp"
|
||||
Then the resource add command should succeed
|
||||
And the resource add output should contain "Added resource"
|
||||
And the resource add output should not contain "Resource type not found"
|
||||
|
||||
@tdd_issue @tdd_issue_2911
|
||||
Scenario: fs-directory can reference fs-mount as parent type after bootstrap
|
||||
Given a fresh in-memory resource registry with bootstrap
|
||||
When I query the fs bootstrap resource type registry for "fs-directory"
|
||||
Then the bootstrap fs resource type "fs-directory" should exist
|
||||
And the bootstrap fs resource type parent_types should contain "fs-mount"
|
||||
@@ -207,3 +207,42 @@ def step_output_should_not_contain(context: Context, text: str) -> None:
|
||||
f"Output unexpectedly contained '{text}'. "
|
||||
f"Full output: {context.bootstrap_fs_output!r}"
|
||||
)
|
||||
|
||||
|
||||
@then("the bootstrap fs resource type user_addable should be true")
|
||||
def step_type_user_addable_true(context: Context) -> None:
|
||||
"""Assert the resource type is user-addable."""
|
||||
spec = context.bootstrap_fs_type_spec
|
||||
assert spec.user_addable is True, (
|
||||
f"Expected user_addable to be True, got {spec.user_addable!r}"
|
||||
)
|
||||
|
||||
|
||||
@then('the bootstrap fs resource type child_types should contain "{child}"')
|
||||
def step_type_child_types_contain(context: Context, child: str) -> None:
|
||||
"""Assert the resource type has the given child type."""
|
||||
spec = context.bootstrap_fs_type_spec
|
||||
child_types = spec.child_types or []
|
||||
assert child in child_types, (
|
||||
f"Expected child_types to contain '{child}', got {child_types!r}"
|
||||
)
|
||||
|
||||
|
||||
@then("the bootstrap fs resource type parent_types should be empty")
|
||||
def step_type_parent_types_empty(context: Context) -> None:
|
||||
"""Assert the resource type has no parent types."""
|
||||
spec = context.bootstrap_fs_type_spec
|
||||
parent_types = spec.parent_types or []
|
||||
assert len(parent_types) == 0, (
|
||||
f"Expected parent_types to be empty, got {parent_types!r}"
|
||||
)
|
||||
|
||||
|
||||
@then('the bootstrap fs resource type parent_types should contain "{parent}"')
|
||||
def step_type_parent_types_contain(context: Context, parent: str) -> None:
|
||||
"""Assert the resource type has the given parent type."""
|
||||
spec = context.bootstrap_fs_type_spec
|
||||
parent_types = spec.parent_types or []
|
||||
assert parent in parent_types, (
|
||||
f"Expected parent_types to contain '{parent}', got {parent_types!r}"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
*** Settings ***
|
||||
Documentation Integration tests for built-in fs-mount type bootstrap (bug #2911).
|
||||
... These tests verify that bootstrap_builtin_types() correctly seeds
|
||||
... fs-mount into the registry and that resource registration succeeds
|
||||
... after bootstrap.
|
||||
Library Process
|
||||
Library OperatingSystem
|
||||
Resource ${CURDIR}/common.resource
|
||||
|
||||
Suite Setup Setup Test Environment
|
||||
Suite Teardown Cleanup Test Environment
|
||||
|
||||
*** Test Cases ***
|
||||
Bootstrap Seeds Fs Mount Type Into Registry
|
||||
[Documentation] Verify that bootstrap_builtin_types() seeds fs-mount into the
|
||||
... database so that show_type("fs-mount") succeeds.
|
||||
${script}= Catenate SEPARATOR=\n
|
||||
... from sqlalchemy import create_engine
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.application.services.resource_registry_service import ResourceRegistryService
|
||||
... engine = create_engine("sqlite:///:memory:", echo=False)
|
||||
... Base.metadata.create_all(engine)
|
||||
... factory = sessionmaker(bind=engine, expire_on_commit=False)
|
||||
... service = ResourceRegistryService(session_factory=factory)
|
||||
... registered = [t.name for t in service.list_types()]
|
||||
... 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)
|
||||
... 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}"
|
||||
... assert spec.user_addable is True, f"user_addable: {spec.user_addable}"
|
||||
... print("fs-mount bootstrap validated successfully")
|
||||
${result}= Run Process ${PYTHON} -c ${script} timeout=120s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0 fs-mount bootstrap failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} fs-mount bootstrap validated successfully
|
||||
|
||||
Fs Mount Has Correct Child Types After Bootstrap
|
||||
[Documentation] Verify that fs-mount has fs-directory as a child type.
|
||||
${script}= Catenate SEPARATOR=\n
|
||||
... from sqlalchemy import create_engine
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.application.services.resource_registry_service import ResourceRegistryService
|
||||
... engine = create_engine("sqlite:///:memory:", echo=False)
|
||||
... Base.metadata.create_all(engine)
|
||||
... factory = sessionmaker(bind=engine, expire_on_commit=False)
|
||||
... service = ResourceRegistryService(session_factory=factory)
|
||||
... spec = service.show_type("fs-mount")
|
||||
... assert "fs-directory" in spec.child_types, f"child_types: {spec.child_types}"
|
||||
... assert spec.parent_types == [], f"parent_types should be empty: {spec.parent_types}"
|
||||
... print("fs-mount child/parent types validated successfully")
|
||||
${result}= Run Process ${PYTHON} -c ${script} timeout=120s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0 fs-mount child types check failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} fs-mount child/parent types validated successfully
|
||||
|
||||
Resource Add Fs Mount Succeeds After Bootstrap
|
||||
[Documentation] Verify that after bootstrap, registering an fs-mount resource
|
||||
... instance succeeds (no "Resource type not found" error).
|
||||
${script}= Catenate SEPARATOR=\n
|
||||
... from sqlalchemy import create_engine
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.application.services.resource_registry_service import ResourceRegistryService
|
||||
... engine = create_engine("sqlite:///:memory:", echo=False)
|
||||
... Base.metadata.create_all(engine)
|
||||
... factory = sessionmaker(bind=engine, expire_on_commit=False)
|
||||
... service = ResourceRegistryService(session_factory=factory)
|
||||
... service.bootstrap_builtin_types()
|
||||
... resource = service.register_resource(
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}type_name="fs-mount",
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}name="local/test-mount",
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}location="/tmp",
|
||||
... ${SPACE}${SPACE}${SPACE}${SPACE}description="Test fs-mount resource",
|
||||
... )
|
||||
... assert resource.resource_type_name == "fs-mount", f"type: {resource.resource_type_name}"
|
||||
... assert resource.name == "local/test-mount", f"name: {resource.name}"
|
||||
... print("resource add fs-mount succeeded after bootstrap")
|
||||
${result}= Run Process ${PYTHON} -c ${script} timeout=120s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0 resource add fs-mount failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} resource add fs-mount succeeded after bootstrap
|
||||
|
||||
Fs Directory Has Fs Mount As Parent Type After Bootstrap
|
||||
[Documentation] Verify that fs-directory lists fs-mount as an allowed parent type.
|
||||
${script}= Catenate SEPARATOR=\n
|
||||
... from sqlalchemy import create_engine
|
||||
... from sqlalchemy.orm import sessionmaker
|
||||
... from cleveragents.infrastructure.database.models import Base
|
||||
... from cleveragents.application.services.resource_registry_service import ResourceRegistryService
|
||||
... engine = create_engine("sqlite:///:memory:", echo=False)
|
||||
... Base.metadata.create_all(engine)
|
||||
... factory = sessionmaker(bind=engine, expire_on_commit=False)
|
||||
... service = ResourceRegistryService(session_factory=factory)
|
||||
... spec = service.show_type("fs-directory")
|
||||
... assert "fs-mount" in spec.parent_types, f"fs-mount not in fs-directory parent_types: {spec.parent_types}"
|
||||
... print("fs-directory parent_types includes fs-mount")
|
||||
${result}= Run Process ${PYTHON} -c ${script} timeout=120s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0 fs-directory parent types check failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} fs-directory parent_types includes fs-mount
|
||||
@@ -72,6 +72,38 @@ __all__ = [
|
||||
# ===== Git / Filesystem / Container types (unchanged) =====
|
||||
|
||||
_GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
{
|
||||
"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,
|
||||
"scan_depth": 1,
|
||||
"rules": [
|
||||
{"type": "fs-directory", "pattern": "*/"},
|
||||
],
|
||||
},
|
||||
"handler": "cleveragents.resource.handlers.fs_mount:FsMountHandler",
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": True,
|
||||
"sandbox": True,
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "git-checkout",
|
||||
"description": "A local git checkout (cloned repository or worktree).",
|
||||
@@ -125,7 +157,7 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
"description": "Path to the directory.",
|
||||
},
|
||||
],
|
||||
"parent_types": ["git-checkout", "fs-directory"],
|
||||
"parent_types": ["git-checkout", "fs-mount", "fs-directory"],
|
||||
"child_types": [
|
||||
"fs-directory",
|
||||
"fs-file",
|
||||
|
||||
Reference in New Issue
Block a user