feat(tool): add tool-level execution environment preferences #879

Closed
opened 2026-03-13 22:59:35 +00:00 by freemo · 2 comments
Owner

Metadata

  • Commit Message: feat(tool): add tool-level execution environment preferences
  • Branch: feature/m5-tool-env-prefs

Background and Context

The specification defines three tool-level execution environment preferences: required (tool MUST run in a container — fail if unavailable), preferred (try container, fall back to host), and specific (target a named container resource). Currently, ToolSpec has no env_preference or execution_environment field. The ToolRunner accepts tool_env as a plain str | None parameter with no structured preference model.

This feature is needed for tools that require a specific execution environment (e.g., build tools that must run inside a devcontainer, database tools that need access to a specific container).

Expected Behavior

  • required: Tool fails with a clear error if no container is available
  • preferred: Tool tries container, falls back to host if unavailable
  • specific(resource_name): Tool targets a named container resource
  • Preference is declared in the tool YAML config under execution_environment
  • ToolRunner consults the preference during execution

Acceptance Criteria

  • ToolSpec has an execution_environment field with preference model
  • required preference raises error when no container available
  • preferred preference falls back gracefully to host
  • specific preference targets a named container resource
  • Tool YAML config supports execution_environment declaration
  • ToolRunner integrates preference into environment resolution

Subtasks

  • Add ExecutionEnvironmentPreference model (required/preferred/specific)
  • Add execution_environment field to ToolSpec
  • Update tool YAML schema to support the new field
  • Implement preference resolution in ToolRunner.execute()
  • Wire into ExecutionEnvironmentResolver (level 0: tool preference)
  • Tests (Behave): Add scenarios for each preference mode
  • Verify coverage >= 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
## Metadata - **Commit Message**: `feat(tool): add tool-level execution environment preferences` - **Branch**: `feature/m5-tool-env-prefs` ## Background and Context The specification defines three tool-level execution environment preferences: `required` (tool MUST run in a container — fail if unavailable), `preferred` (try container, fall back to host), and `specific` (target a named container resource). Currently, `ToolSpec` has no `env_preference` or `execution_environment` field. The `ToolRunner` accepts tool_env as a plain `str | None` parameter with no structured preference model. This feature is needed for tools that require a specific execution environment (e.g., build tools that must run inside a devcontainer, database tools that need access to a specific container). ## Expected Behavior - `required`: Tool fails with a clear error if no container is available - `preferred`: Tool tries container, falls back to host if unavailable - `specific(resource_name)`: Tool targets a named container resource - Preference is declared in the tool YAML config under `execution_environment` - `ToolRunner` consults the preference during execution ## Acceptance Criteria - [ ] `ToolSpec` has an `execution_environment` field with preference model - [ ] `required` preference raises error when no container available - [ ] `preferred` preference falls back gracefully to host - [ ] `specific` preference targets a named container resource - [ ] Tool YAML config supports `execution_environment` declaration - [ ] ToolRunner integrates preference into environment resolution ## Subtasks - [ ] Add `ExecutionEnvironmentPreference` model (required/preferred/specific) - [ ] Add `execution_environment` field to `ToolSpec` - [ ] Update tool YAML schema to support the new field - [ ] Implement preference resolution in `ToolRunner.execute()` - [ ] Wire into `ExecutionEnvironmentResolver` (level 0: tool preference) - [ ] Tests (Behave): Add scenarios for each preference mode - [ ] Verify coverage >= 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done.
freemo added this to the v3.5.0 milestone 2026-03-13 23:00:00 +00:00
Member

Implementation Notes

Architecture

New model (execution_environment_preference.py, 73 lines): EnvironmentPreferenceMode StrEnum with 4 modes (REQUIRED, PREFERRED, SPECIFIC, NONE). ExecutionEnvironmentPreference frozen Pydantic model with mode and target_resource fields. Model validator ensures target_resource required iff mode is SPECIFIC.

ToolSpec integration (runtime.py): Added execution_environment: ExecutionEnvironmentPreference with default NONE. This makes the preference intrinsic to the tool definition rather than caller-supplied.

Domain Tool model (tool.py): Same field added. Tool.from_config() parses execution_environment from YAML config dicts.

ToolRunner integration (runner.py): The execute() method now consults spec.execution_environment.mode BEFORE calling the environment resolver:

  • REQUIRED: Resolved env must be CONTAINER or ContainerUnavailableError raised
  • PREFERRED: Try container via resolver, catch ContainerUnavailableError and fall back to HOST
  • SPECIFIC: Override tool_env with target_resource (routes to named container)
  • NONE: Current behavior unchanged

Design Decisions

  1. Preference is intrinsic, not caller-supplied: Unlike the existing tool_env parameter (passed by caller), the new preference lives on the ToolSpec itself — declared in the tool's YAML config and consulted automatically
  2. NONE as default: All existing tools continue working unchanged since NONE means "no preference, use the standard resolution chain"
  3. SPECIFIC validates eagerly: The model validator rejects SPECIFIC mode without a target_resource at construction time, not at execution time

Quality Gates

Session Result
lint PASS
typecheck PASS (0 errors)
unit_tests PASS (10,833 scenarios, 27 new)
integration_tests PASS (12 new tests)

Commit

3bfd0775 on branch feature/m5-tool-env-prefs

PR

PR #970

## Implementation Notes ### Architecture **New model** (`execution_environment_preference.py`, 73 lines): `EnvironmentPreferenceMode` StrEnum with 4 modes (REQUIRED, PREFERRED, SPECIFIC, NONE). `ExecutionEnvironmentPreference` frozen Pydantic model with `mode` and `target_resource` fields. Model validator ensures `target_resource` required iff mode is SPECIFIC. **ToolSpec integration** (`runtime.py`): Added `execution_environment: ExecutionEnvironmentPreference` with default NONE. This makes the preference intrinsic to the tool definition rather than caller-supplied. **Domain Tool model** (`tool.py`): Same field added. `Tool.from_config()` parses `execution_environment` from YAML config dicts. **ToolRunner integration** (`runner.py`): The `execute()` method now consults `spec.execution_environment.mode` BEFORE calling the environment resolver: - REQUIRED: Resolved env must be CONTAINER or `ContainerUnavailableError` raised - PREFERRED: Try container via resolver, catch `ContainerUnavailableError` and fall back to HOST - SPECIFIC: Override `tool_env` with `target_resource` (routes to named container) - NONE: Current behavior unchanged ### Design Decisions 1. **Preference is intrinsic, not caller-supplied**: Unlike the existing `tool_env` parameter (passed by caller), the new preference lives on the `ToolSpec` itself — declared in the tool's YAML config and consulted automatically 2. **NONE as default**: All existing tools continue working unchanged since NONE means "no preference, use the standard resolution chain" 3. **SPECIFIC validates eagerly**: The model validator rejects SPECIFIC mode without a `target_resource` at construction time, not at execution time ### Quality Gates | Session | Result | |---|---| | lint | PASS | | typecheck | PASS (0 errors) | | unit_tests | PASS (10,833 scenarios, 27 new) | | integration_tests | PASS (12 new tests) | ### Commit `3bfd0775` on branch `feature/m5-tool-env-prefs` ### PR [PR #970](https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/970)
freemo self-assigned this 2026-04-02 06:13:56 +00:00
Author
Owner

[Backlog Groomer - groomer-1] Closing this issue. PR #970 (feat(tool): add tool-level execution environment preferences) was merged on 2026-03-20. All acceptance criteria are met per the implementation notes in the previous comment. Work is complete.

**[Backlog Groomer - groomer-1]** Closing this issue. PR #970 (`feat(tool): add tool-level execution environment preferences`) was merged on 2026-03-20. All acceptance criteria are met per the implementation notes in the previous comment. Work is complete.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core#879
No description provided.