Files
cleveragents-core/features/consolidated_binding_resolution.feature
freemo 3224f2136e
CI / lint (pull_request) Successful in 27s
CI / typecheck (pull_request) Successful in 52s
CI / quality (pull_request) Successful in 32s
CI / security (pull_request) Successful in 59s
CI / build (pull_request) Successful in 29s
CI / helm (pull_request) Successful in 23s
CI / unit_tests (pull_request) Successful in 6m58s
CI / e2e_tests (pull_request) Successful in 19m39s
CI / integration_tests (pull_request) Successful in 22m49s
CI / coverage (pull_request) Successful in 10m43s
CI / docker (pull_request) Successful in 1m23s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 56m57s
fix(resource-registry): use full inheritance chain in contextual binding type compatibility
Replace the flat `parent_types` membership check in
`BindingResolutionService._is_type_compatible` with a call to
`self._registry.is_subtype_of()`, which uses
`resolve_inheritance_chain()` from `cleveragents.resource.inheritance`
to walk the full ADR-042 inheritance chain.

Previously, a tool requiring type `container-instance` would fail to
bind a `devcontainer-instance` resource because the check only looked
at the immediate `parent_types` list rather than traversing the full
chain.  The fix ensures multi-level inheritance (e.g.
`local/special-dev-container` → `devcontainer-instance` →
`container-instance`) is correctly resolved for contextual, static,
and parameter bindings.

Changes:
- `binding_resolution_service.py`: `_is_type_compatible` now delegates
  to `registry.is_subtype_of()`; removed unused `ResourceTypeSpec`
  import.
- `consolidated_binding_resolution.feature`: 4 new scenarios covering
  2-level chain, 3-level chain, unrelated-type rejection, and static
  binding via inheritance chain.
- `binding_resolution_steps.py`: mock registry exposes
  `is_subtype_of()` backed by the real inheritance engine; new step
  definitions for inheritance chain setup.

ISSUES CLOSED: #2929
2026-04-05 17:55:54 +00:00

358 lines
18 KiB
Gherkin

Feature: Consolidated Binding Resolution
Combined scenarios from: binding_resolution, binding_resolution_coverage
# ============================================================
# Originally from: binding_resolution.feature
# Feature: Tool Resource Binding Resolution
# ============================================================
@contextual @single_match
Scenario: Contextual binding with single matching resource
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/my-project" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000001 | git-checkout | repo |
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
When the bindings are resolved
Then the binding for slot "repo" should have mode "contextual"
And the binding for slot "repo" should have resource_id "01HGZ6FE0AQDYTR4BX00000001"
And the binding for slot "repo" should not be deferred
# ---------------------------------------------------------------------------
# Contextual binding -- multiple matches with alias hint
# ---------------------------------------------------------------------------
@contextual @alias_hint
Scenario: Contextual binding with multiple matches uses alias hint
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/multi" with linked resources:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000010 | git-checkout | repo |
| 01HGZ6FE0AQDYTR4BX00000011 | git-checkout | backup |
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
When the bindings are resolved
Then the binding for slot "repo" should have resource_id "01HGZ6FE0AQDYTR4BX00000010"
# ---------------------------------------------------------------------------
# Contextual binding -- no match (error)
# ---------------------------------------------------------------------------
@contextual @no_match @error_handling
Scenario: Contextual binding with no matching resource raises error
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/empty" with no linked resources
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
When the binding resolution is attempted
Then a binding ValidationError should be raised mentioning "No resource of type"
# ---------------------------------------------------------------------------
# Static binding resolution
# ---------------------------------------------------------------------------
@static
Scenario: Static binding resolves named resource
Given a mock resource registry
And a binding resolution service using the registry
Given a resource "local/shared-config" of type "fs-directory" in the registry
And a tool "local/deployer" with a static slot "config_dir" bound to "local/shared-config" of type "fs-directory"
And a project "local/proj" with no linked resources
When the bindings are resolved
Then the binding for slot "config_dir" should have mode "static"
And the binding for slot "config_dir" should have resource_id "01HGZ6FE0AQDYTR4BX00000020"
And the binding for slot "config_dir" should not be deferred
# ---------------------------------------------------------------------------
# Parameter binding (deferred)
# ---------------------------------------------------------------------------
@parameter @deferred
Scenario: Parameter binding is deferred when no invocation params given
Given a mock resource registry
And a binding resolution service using the registry
Given a tool "local/builder" with a parameter slot "target" of type "fs-directory"
And a project "local/proj" with no linked resources
When the bindings are resolved
Then the binding for slot "target" should have mode "parameter"
And the binding for slot "target" should be deferred
And the binding for slot "target" should have no resource_id
# ---------------------------------------------------------------------------
# Type compatibility -- pass (sub-type)
# ---------------------------------------------------------------------------
@type_compat @pass
Scenario: Type compatibility passes for sub-type via parent_types
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/typed" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000030 | fs-directory | repo |
And the resource type "fs-directory" has parent_types including "git-checkout"
And a tool "local/compat" with a contextual slot "repo" of type "git-checkout"
When the bindings are resolved
Then the binding for slot "repo" should have resource_id "01HGZ6FE0AQDYTR4BX00000030"
# ---------------------------------------------------------------------------
# Type compatibility -- fail
# ---------------------------------------------------------------------------
@type_compat @fail @error_handling
Scenario: Type compatibility fails for incompatible types
Given a mock resource registry
And a binding resolution service using the registry
Given a resource "local/wrong-type" of type "fs-file" in the registry
And the resource type "fs-file" has no parent_types
And a tool "local/strict" with a static slot "data" bound to "local/wrong-type" of type "git-checkout"
And a project "local/proj" with no linked resources
When the binding resolution is attempted
Then a binding ValidationError should be raised mentioning "Type mismatch"
# ---------------------------------------------------------------------------
# Mixed bindings on same tool
# ---------------------------------------------------------------------------
@mixed
Scenario: Mixed bindings on same tool resolve correctly
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/mixed" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000040 | git-checkout | repo |
And a resource "local/shared-config" of type "fs-directory" in the registry
And a tool "local/multi-bind" with mixed slots:
| name | resource_type | binding | static_resource |
| repo | git-checkout | contextual | |
| config | fs-directory | static | local/shared-config |
| output | fs-directory | parameter | |
When the bindings are resolved
Then there should be 3 binding results
And the binding for slot "repo" should have mode "contextual"
And the binding for slot "config" should have mode "static"
And the binding for slot "output" should have mode "parameter"
And the binding for slot "output" should be deferred
# ============================================================
# Originally from: binding_resolution_coverage.feature
# Feature: Binding Resolution Service - Coverage Gaps
# ============================================================
@static @error_handling
Scenario: Static binding raises ValidationError for unknown resource
Given a mock resource registry
And a binding resolution service using the registry
Given a tool "local/deployer" with a static slot "cfg" bound to "local/nonexistent" of type "fs-directory"
And a project "local/proj" with no linked resources
When the binding resolution is attempted
Then a binding ValidationError should be raised mentioning "Static binding for slot"
# ---------------------------------------------------------------------------
# Parameter binding -- provided ref not found (lines 198-201)
# ---------------------------------------------------------------------------
@parameter @error_handling
Scenario: Parameter binding raises ValidationError for unknown ref
Given a mock resource registry
And a binding resolution service using the registry
Given a tool "local/builder" with a parameter slot "target" of type "fs-directory"
And a project "local/proj" with no linked resources
When the bindings are resolved with invocation params:
| slot | ref |
| target | local/does-not-exist |
Then a binding ValidationError should be raised mentioning "Parameter binding for slot"
# ---------------------------------------------------------------------------
# Parameter binding -- valid ref resolves (lines 209, 214)
# ---------------------------------------------------------------------------
@parameter @resolve
Scenario: Parameter binding resolves successfully with valid ref
Given a mock resource registry
And a binding resolution service using the registry
Given a named resource "01HGZ6FE0AQDYTR4BX00000060" called "local/my-dir" of type "fs-directory" in the registry
And a tool "local/builder" with a parameter slot "target" of type "fs-directory"
And a project "local/proj" with no linked resources
When the bindings are resolved with invocation params:
| slot | ref |
| target | local/my-dir |
Then the binding for slot "target" should have mode "parameter"
And the binding for slot "target" should have resource_id "01HGZ6FE0AQDYTR4BX00000060"
And the binding for slot "target" should not be deferred
# ---------------------------------------------------------------------------
# Contextual binding -- no match, slot NOT required (line 274)
# ---------------------------------------------------------------------------
@contextual @optional
Scenario: Contextual binding returns None resource when slot is not required
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/empty" with no linked resources
And a tool "local/reader" with an optional contextual slot "extras" of type "git-checkout"
When the bindings are resolved
Then the binding for slot "extras" should have mode "contextual"
And the binding for slot "extras" should have no resource_id
And the binding for slot "extras" should not be deferred
# ---------------------------------------------------------------------------
# _find_matching_resources -- NotFoundError skipped (lines 298-299)
# ---------------------------------------------------------------------------
@contextual @not_found_skip
Scenario: Contextual binding skips linked resources that raise NotFoundError
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/mixed" with linked resources including a missing one:
| resource_id | type_name | alias | exists |
| 01HGZ6FE0AQDYTR4BX00000070 | git-checkout | gone | false |
| 01HGZ6FE0AQDYTR4BX00000071 | git-checkout | repo | true |
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
When the bindings are resolved
Then the binding for slot "repo" should have resource_id "01HGZ6FE0AQDYTR4BX00000071"
And the binding for slot "repo" should not be deferred
# ---------------------------------------------------------------------------
# _disambiguate -- name suffix matching (lines 325-329)
# ---------------------------------------------------------------------------
@contextual @disambiguate @name_suffix
Scenario: Disambiguation matches slot name against resource name suffix
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/multi" with named linked resources:
| resource_id | type_name | alias | resource_name |
| 01HGZ6FE0AQDYTR4BX00000080 | git-checkout | alpha | local/frontend/repo |
| 01HGZ6FE0AQDYTR4BX00000081 | git-checkout | beta | local/backend/other |
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
When the bindings are resolved
Then the binding for slot "repo" should have resource_id "01HGZ6FE0AQDYTR4BX00000080"
And the binding for slot "repo" should have resource_name "local/frontend/repo"
# ---------------------------------------------------------------------------
# _disambiguate -- ambiguous (lines 337-338)
# ---------------------------------------------------------------------------
@contextual @disambiguate @ambiguous @error_handling
Scenario: Disambiguation raises ValidationError for ambiguous candidates
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/multi" with named linked resources:
| resource_id | type_name | alias | resource_name |
| 01HGZ6FE0AQDYTR4BX00000090 | git-checkout | alpha | local/first |
| 01HGZ6FE0AQDYTR4BX00000091 | git-checkout | beta | local/second |
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
When the binding resolution is attempted
Then a binding ValidationError should be raised mentioning "Ambiguous contextual binding"
# ---------------------------------------------------------------------------
# _is_type_compatible -- is_subtype_of returns False for unknown type
# ---------------------------------------------------------------------------
@type_compat @not_found
Scenario: Type compatibility returns False for unknown type
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/typed" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000095 | unknown-type | repo |
And the resource type "unknown-type" is removed from the registry
And a tool "local/reader" with a contextual slot "repo" of type "git-checkout"
And the slot "repo" is required
When the binding resolution is attempted
Then a binding ValidationError should be raised mentioning "No resource of type"
# ---------------------------------------------------------------------------
# Multi-level inheritance -- contextual binding (ADR-042 fix)
# ---------------------------------------------------------------------------
@type_compat @inheritance @multi_level
Scenario: Contextual binding resolves via two-level inheritance chain
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/typed" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000100 | devcontainer-instance | env |
And the resource type "container-instance" inherits from nothing
And the resource type "devcontainer-instance" inherits from "container-instance"
And a tool "local/runner" with a contextual slot "env" of type "container-instance"
When the bindings are resolved
Then the binding for slot "env" should have resource_id "01HGZ6FE0AQDYTR4BX00000100"
And the binding for slot "env" should have mode "contextual"
And the binding for slot "env" should not be deferred
@type_compat @inheritance @multi_level
Scenario: Contextual binding resolves via three-level inheritance chain
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/typed" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000101 | local/special-dev-container | env |
And the resource type "container-instance" inherits from nothing
And the resource type "devcontainer-instance" inherits from "container-instance"
And the resource type "local/special-dev-container" inherits from "devcontainer-instance"
And a tool "local/runner" with a contextual slot "env" of type "container-instance"
When the bindings are resolved
Then the binding for slot "env" should have resource_id "01HGZ6FE0AQDYTR4BX00000101"
And the binding for slot "env" should have mode "contextual"
And the binding for slot "env" should not be deferred
@type_compat @inheritance @unrelated_type
Scenario: Contextual binding rejects unrelated type even with inheritance present
Given a mock resource registry
And a binding resolution service using the registry
Given a project "local/typed" with a linked resource:
| resource_id | type_name | alias |
| 01HGZ6FE0AQDYTR4BX00000102 | fs-directory | dir |
And the resource type "container-instance" inherits from nothing
And a tool "local/runner" with a contextual slot "env" of type "container-instance"
And the slot "env" is required
When the binding resolution is attempted
Then a binding ValidationError should be raised mentioning "No resource of type"
@type_compat @inheritance @static_binding
Scenario: Static binding resolves via two-level inheritance chain
Given a mock resource registry
And a binding resolution service using the registry
Given a resource "local/my-devcontainer" of type "devcontainer-instance" in the registry
And the resource type "container-instance" inherits from nothing
And the resource type "devcontainer-instance" inherits from "container-instance"
And a tool "local/runner" with a static slot "env" bound to "local/my-devcontainer" of type "container-instance"
And a project "local/proj" with no linked resources
When the bindings are resolved
Then the binding for slot "env" should have mode "static"
And the binding for slot "env" should not be deferred
@type_compat @inheritance @parameter_binding
Scenario: Parameter binding resolves via two-level inheritance chain
Given a mock resource registry
And a binding resolution service using the registry
Given a named resource "01HGZ6FE0AQDYTR4BX00000103" called "local/my-devcontainer" of type "devcontainer-instance" in the registry
And the resource type "container-instance" inherits from nothing
And the resource type "devcontainer-instance" inherits from "container-instance"
And a tool "local/runner" with a parameter slot "env" of type "container-instance"
And a project "local/proj" with no linked resources
When the bindings are resolved with invocation params:
| slot | ref |
| env | local/my-devcontainer |
Then the binding for slot "env" should have mode "parameter"
And the binding for slot "env" should not be deferred