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

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:
2026-04-05 07:29:35 +00:00
parent 8c079943e6
commit f373e3a527
4 changed files with 223 additions and 1 deletions
@@ -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",