fix(schema): update actor, skill, and tool name validators to accept [[server:]namespace/]name format #9175

Merged
HAL9000 merged 6 commits from fix/name-validators-server-qualified-format into master 2026-04-23 12:40:00 +00:00
8 changed files with 102 additions and 12 deletions
+8
View File
@@ -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
+28
View File
@@ -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
# ────────────────────────────────────────────────────────────
+10
View File
@@ -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 ----
+16
View File
@@ -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
+1
View File
@@ -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
"""
+34 -10
View File
@@ -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}"
+1 -1
View File
@@ -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
+4 -1
View File
@@ -38,7 +38,10 @@ logger = logging.getLogger(__name__)
# ────────────────────────────────────────────────────────────
#: Pattern for ``<namespace>/<name>`` 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_]*)\}")