diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ac313e96..bc61e3e2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **Bug Hunt Pool Supervisor Non-Blocking Tracking**: Updated `bug-hunt-pool-supervisor` to make the automation tracking step non-blocking. The `automation-tracking-manager` call in step 5 is now best-effort — if it does not complete within a reasonable time or fails, the supervisor skips it and continues to the next cycle. Added explicit rule 9 clarifying that tracking must never block the main loop. Core functionality (module scanning and worker dispatch) takes priority over status reporting. +- **Name Validator Server-Qualified Format** (#9074): Updated actor, skill, and tool name + validators to accept the spec-required `[[server:]namespace/]name` format. Previously, + server-qualified names like `dev:freemo/custom-analysis` were incorrectly rejected. + Added BDD scenarios for server-qualified name acceptance and rejection. All three + validators (`ActorConfigSchema.validate_name`, `NAMESPACED_NAME_RE`, + `_TOOL_NAME_PATTERN`) now correctly support optional server prefixes while maintaining + backward compatibility with existing `namespace/name` names. + - **Automation Profile Silent Fallback** (#8232): `_resolve_profile_for_plan` in `PlanLifecycleService` now raises a clear `ValidationError` when a plan's automation profile name is not a known built-in profile, instead of silently diff --git a/features/actor_schema.feature b/features/actor_schema.feature index 0d833efb0..59b1ab6ac 100644 --- a/features/actor_schema.feature +++ b/features/actor_schema.feature @@ -197,6 +197,34 @@ Feature: Actor YAML schema validation Then the actor schema validation should fail And the validation error should contain "namespaced" + Scenario: Accept actor with server-qualified name + Given an actor YAML string with name "dev:freemo/custom-analysis" + When I validate the actor schema + Then the actor schema validation should succeed + + Scenario: Accept actor with server-qualified name using cleverthis server + Given an actor YAML string with name "cleverthis:local/my-actor" + When I validate the actor schema + Then the actor schema validation should succeed + + Scenario: Reject actor with server-qualified name but multiple slashes + Given an actor YAML string with name "dev:namespace/sub/actor" + When I validate the actor schema + Then the actor schema validation should fail + And the validation error should contain "namespaced" + + Scenario: Reject actor with empty server prefix + Given an actor YAML string with name ":namespace/name" + When I validate the actor schema + Then the actor schema validation should fail + And the validation error should contain "empty server prefix" + + Scenario: Reject actor with server prefix but no namespace slash + Given an actor YAML string with name "dev/server:name" + When I validate the actor schema + Then the actor schema validation should fail + And the validation error should contain "namespace/name after server prefix" + # ──────────────────────────────────────────────────────────── # Invalid LLM Actor scenarios # ──────────────────────────────────────────────────────────── diff --git a/features/consolidated_tool.feature b/features/consolidated_tool.feature index b602a6087..6236ffc74 100644 --- a/features/consolidated_tool.feature +++ b/features/consolidated_tool.feature @@ -153,6 +153,16 @@ Feature: Consolidated Tool And the tool model namespace should be "my-ns" And the tool model short name should be "my_tool" + + Scenario: Tool name accepts server-qualified format + When I create a tool with name "dev:freemo/custom-analysis" and source "builtin" + Then the tool model should be created + + + Scenario: Tool name accepts server-qualified format with cleverthis server + When I create a tool with name "cleverthis:local/run-migrations" and source "builtin" + Then the tool model should be created + # ---- Source-conditional field requirements ---- diff --git a/features/skill_schema.feature b/features/skill_schema.feature index 144dcf8c2..e09d9d4f0 100644 --- a/features/skill_schema.feature +++ b/features/skill_schema.feature @@ -131,6 +131,22 @@ Feature: Skill YAML schema validation Then the skill schema validation should fail And the skill schema error should mention "namespace/name" + Scenario: Accept skill with server-qualified name + Given a skill YAML string with name "dev:freemo/custom-skill" + When I validate the skill schema + Then the skill schema validation should succeed + + Scenario: Accept skill with server-qualified name using cleverthis server + Given a skill YAML string with name "cleverthis:local/my-skill" + When I validate the skill schema + Then the skill schema validation should succeed + + Scenario: Reject skill with server-qualified name but multiple slashes + Given a skill YAML string with name "dev:namespace/sub/skill" + When I validate the skill schema expecting failure + Then the skill schema validation should fail + And the skill schema error should mention "namespace/name" + Scenario: Invalid namespaced name with special characters Given a skill YAML string with name "local/bad name!!" When I validate the skill schema expecting failure diff --git a/features/steps/actor_schema_steps.py b/features/steps/actor_schema_steps.py index cd1995425..44df790ce 100644 --- a/features/steps/actor_schema_steps.py +++ b/features/steps/actor_schema_steps.py @@ -404,6 +404,7 @@ def step_given_actor_with_name(context: Context, name: str) -> None: name: {name} type: llm description: Test actor +provider: openai model: gpt-4 """ diff --git a/src/cleveragents/actor/schema.py b/src/cleveragents/actor/schema.py index 81789c6d8..865a79c86 100644 --- a/src/cleveragents/actor/schema.py +++ b/src/cleveragents/actor/schema.py @@ -810,21 +810,45 @@ class ActorConfigSchema(BaseModel): @field_validator("name") @classmethod def validate_name(cls, v: str) -> str: - """Ensure actor name follows namespace/name format.""" + """Ensure actor name follows [[server:]namespace/]name format. + + Supports: + - namespace/name (e.g., 'local/my-actor') + - server:namespace/name (e.g., 'dev:freemo/custom-analysis') + """ if "/" not in v: msg = f"Actor name must be namespaced (namespace/name): {v}" raise ValueError(msg) - # Check for exactly one slash - parts = v.split("/") - if len(parts) != 2: - msg = ( - f"Actor name must be namespaced with exactly one slash " - f"(namespace/name): {v}" - ) - raise ValueError(msg) + # Split by colon first to extract optional server prefix + if ":" in v: + server_part, rest = v.split(":", 1) + if not server_part: + msg = f"Actor name has empty server prefix: {v}" + raise ValueError(msg) + # rest should be namespace/name + if "/" not in rest: + msg = f"Actor name must have namespace/name after server prefix: {v}" + raise ValueError(msg) + parts = rest.split("/") + if len(parts) != 2: + msg = ( + f"Actor name must be namespaced with exactly one slash " + f"(namespace/name after optional server prefix): {v}" + ) + raise ValueError(msg) + namespace, name = parts + else: + # No server prefix, just namespace/name + parts = v.split("/") + if len(parts) != 2: + msg = ( + f"Actor name must be namespaced with exactly one slash " + f"(namespace/name): {v}" + ) + raise ValueError(msg) + namespace, name = parts - namespace, name = parts if not namespace or not name: msg = ( f"Actor name must be namespaced with non-empty namespace and name: {v}" diff --git a/src/cleveragents/domain/models/core/tool.py b/src/cleveragents/domain/models/core/tool.py index 0ff79b3a6..84e33e58d 100644 --- a/src/cleveragents/domain/models/core/tool.py +++ b/src/cleveragents/domain/models/core/tool.py @@ -77,7 +77,7 @@ from cleveragents.domain.models.core.execution_environment_preference import ( # Regex patterns # --------------------------------------------------------------------------- -_TOOL_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$") +_TOOL_NAME_PATTERN = re.compile(r"^(?:[a-zA-Z0-9_-]+:)?[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$") # --------------------------------------------------------------------------- # Enums diff --git a/src/cleveragents/skills/schema.py b/src/cleveragents/skills/schema.py index 25cfa722c..7b851b040 100644 --- a/src/cleveragents/skills/schema.py +++ b/src/cleveragents/skills/schema.py @@ -38,7 +38,10 @@ logger = logging.getLogger(__name__) # ──────────────────────────────────────────────────────────── #: Pattern for ``/`` with hyphens, underscores, lowercase alphanum. -NAMESPACED_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9_-]*/[a-z0-9][a-z0-9_-]*$") +#: Supports optional server prefix: ``[server:]namespace/name`` +NAMESPACED_NAME_RE = re.compile( + r"^(?:[a-z0-9][a-z0-9_-]*:)?[a-z0-9][a-z0-9_-]*/[a-z0-9][a-z0-9_-]*$" +) #: Pattern for ``${VAR}`` environment variable references. _ENV_VAR_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")