Dependency Injection ≠ Dynamic Replacement: Managing Agent Capability Recomposition at Runtime
This article explains why dependency injection only handles startup binding, and how a composable agent runtime must detect dependency changes, freeze affected consumers, validate compatibility, rebind with policies, invalidate stale evidence, and escalate to humans—ensuring capabilities never operate on outdated assumptions.
The previous article discussed why plugins must be safely unloadable. However, when a component exits, is replaced, or fails, the impact extends beyond itself. A code-check skill depends on a code-execution tool; the tool depends on a sandbox; the sandbox is constrained by permissions, images, and resource quotas. When models, tools, permissions, or environments change at runtime, capabilities still using them face problems: tools are replaced but skills keep old assumptions; permissions are revoked but old sessions continue calling; environments change but old test reports are still treated as valid evidence.
Dependency Injection Only Solves Initial Binding
Dependency injection is a mature practice: components declare required capabilities (database connections, model clients, tool instances) and a container provides them at initialization. For example, a code-check skill receives a code-execution tool, a rule-query service, and a sandbox configuration. This removes hard-coded implementations and enables test-time substitution. Yet in common usage this remains a startup-time concern. After injection, systems often assume the reference stays valid indefinitely: if the tool name hasn't changed, keep calling; if configuration changes, wait for restart; if the provider fails, let the consumer guess after an error. For agents that load, unload, upgrade, or revoke permissions at runtime, this assumption breaks.
A dependency is not merely "an object to call"; it includes at least six dimensions:
Capability contract – the exact interface, inputs, outputs, and behavioral constraints the consumer relies on.
Provider identity – which concrete tool, model, skill, or service instance is currently bound.
Version & environment – whether versions, rule sets, sandbox images, and configuration baselines still match.
Permission scope – whether the current task and role still have the right to use this capability.
Availability state – whether the provider is available, draining, failed, or awaiting human intervention.
Result impact – which execution states, caches, and verification evidence must be invalidated when the dependency changes.
Therefore, dynamic dependency is not a simple reference swap but a controlled rebinding.
Spatial Composability: Dependency Relationships Must Not Distort Under Change
The Cordis paper calls this "spatial composability": components declare dependencies, and the runtime discovers and manages them when dependencies change. Here "space" is not deployment topology but the runtime capability relationship graph. This can be understood as a reactive coeffect: components declare what they need; the runtime notifies them when the context (the visible capability environment, not the prompt context) changes. The engineering translation for an Agent Runtime is that dependency relationships must be explicit, queryable, triggerable runtime facts—not implicit conventions scattered across code, prompts, and developers' memories.
Concrete Example: Code-Execution Tool Version Upgrade
Assume a development agent is performing "add rate limiting and regression tests for the login API." Current bindings:
consumer: regression-test-skill
requires: code.execute
bound_provider: code.execute-v1
provider_version: 1.8.4
environment: node-22 / sandbox-a / policy-p3
binding_state: active
evidence_scope: test-report-20260819-17Mid-run, code.execute-v1 is replaced by v2 due to a security policy upgrade. The interface name is identical, but the command whitelist, image version, and network access policy have changed. If the runtime merely reinjects by name, it masks critical questions:
Can ongoing tests still complete in the old environment?
Who collects the logs, temporary files, and test reports produced by v1?
Does v2 still satisfy the capability contracts both skills originally depended on?
Can the existing test report still prove the code passes in the "current controlled environment"?
Should tasks that cannot migrate due to permission or environment differences continue, fail, or wait for handling?
The runtime should record the actual binding (as shown above) so it can answer: who produced this result, with what version, and is it still trustworthy after a dependency change.
Handling Dependency Changes: More Than Automatic Switching
When a provider changes, consumer handling strategies must be explicit: <code>Dependency change -> affects external actions or high-risk tasks -> wait for human confirmation -> capability contract, permissions, environment still compatible -> rebind and decide re-validation per rules -> interface adaptable but result credibility affected -> pause node, re-run affected steps -> permission, version, or behavior incompatible -> enter waiting-dependency or blocked state</code> The key is not to keep the system running at all costs, but to avoid silently producing seemingly normal results when dependency conditions have already shifted. For instance, a model version switch may allow continued text generation, but if it performed controlled classification, structured extraction, or validation judgments, the original evaluation baseline and result interpretation must be re-examined. Tool interface compatibility does not imply runtime conditions and evidence scope are naturally compatible. Likewise, permission revocation is not an ordinary call failure: if a data-query tool goes from "allow desensitized fields" to "no access," the consumer should receive an explicit unavailable state, not retry and then fall back to an unauthorized alternative path.
Freeze Affected Consumers Before Recomposition
The previous article covered provider revocation: stop new calls, let consumers complete, cancel, or wait. Dependency replacement follows the same principle but adds a unique question: can the consumer continue working under the new dependency? <code>1. Detect provider version, permission, state, or environment change. 2. Query component dependency graph, freeze new calls from affected consumers. 3. Handle in-flight calls per predefined policy. 4. Validate candidate provider against capability contract, permissions, and isolation requirements. 5. Rebind consumers, mark states and evidence that must be invalidated. 6. Resume continuable nodes; others enter wait, fail, or human-intervention states.</code> Freezing does not mean killing tasks. Safe tests can finish; imminent sensitive-data queries must be blocked immediately; long tasks where correctness becomes uncertain after a dependency change enter a waiting_human state. The runtime executes declared policies—it does not ask the model to improvise "switching tools is probably fine."
Component Dependency Graph ≠ Task Graph
These two graphs are easily conflated. A task graph describes dependencies between requirement analysis, architecture, implementation, test, and acceptance—answering "how things get done." A component dependency graph describes dependencies between models, tools, skills, sessions, sandboxes, permissions, and storage—answering "how the capabilities to do those things are assembled and changed." They interact but cannot substitute for each other. The task graph should not manage tool versions and sandbox permissions; the component graph should not decide whether requirements are satisfied. Merging both into the main agent's context reduces objects short-term but makes dependencies, states, and failure routing inexplicable long-term.
Three Exception Paths That Must Be Designed Explicitly
Dynamic recomposition is not a straight line that always converges. At least three failure paths must be defined upfront: Dependency cycles. If Skill A depends on Tool B, and Tool B's initialization depends on configuration or state from Skill A, retries won't solve it. Identify cycles at assembly time; make one side an explicit interface, use lazy binding, or independent configuration; if inseparable, reject the combination. Version incompatibility. Same provider name ≠ same behavior. Input/output structures, error semantics, resource limits, permission models, and execution environments may all change. Compatibility requires version constraints, adapters, or explicit migration strategies—not just comparing a tool ID. Partial availability. A dependency may be neither fully present nor fully gone. Example: the code-execution tool exists but network permission is revoked; knowledge query works but a data domain is invisible. Consumers should enter degraded, wait, or blocked states per the capability contract, not fold all errors into "tool call failed." Whether these exception paths are explicitly designed determines whether dynamic composition is a controlled change or a faster fault propagation.
Automatic Recomposition Must Have Boundaries
"Reactive" does not mean the runtime can arbitrarily rewire topology. In enterprise agents, model replacement, tool integration, permission upgrades, data-domain switches, and external-action path changes affect security, cost, and responsibility boundaries. Automatic recomposition must be constrained by: Rebind only within approved candidate capabilities and version ranges. Rebinding must satisfy current task, role, and tenant permission conditions. After environment, rule, or provider changes, invalidate caches, session states, and evidence per defined rules. When production operations, sensitive data, or high-risk decisions are affected, escalate to human confirmation. Record dependency changes, binding decisions, affected tasks, and validation results for audit and retrospective. The runtime's goal is not to give the agent "self-evolution" freedom, but to make changes visible, constrainable, and recoverable when capabilities actually change.
First Phase: Validate a Single Real Chain
You don't need a full dynamic hot-swap platform immediately. Pick one real chain—for example, "test skill depends on code-execution tool and restricted sandbox"—and clarify: Record actual bound provider, version, environment, and permissions for each consumer. Define a minimal capability contract and allowed candidate alternatives for each dependency. On provider change, query affected consumers and prevent new erroneous calls. Define boundaries for in-flight tasks: complete, cancel, wait, human confirmation. Link dependency changes with invalidation rules for test reports, caches, and session states. Validate the whole process with one real replacement, permission revocation, or environment failure. Prove the system stops using invalid conditions in one dependency-change scenario before discussing more complex auto-adaptation and cross-process recomposition.
Summary
Dependency injection gives components their required capabilities at startup; a composable runtime ensures that when those capabilities change at runtime, consumers are not left operating on stale assumptions. This is the Agent Runtime's spatial boundary: not letting components reference each other arbitrarily, but making runtime dependency relationships always visible, verifiable, and governable. <code>Dependencies must be declarable; Bindings must be traceable; Changes must be notifiable; Recomposition must have policies; States and evidence must be re-judged by impact scope; Beyond boundaries, explicitly stop and escalate to human.</code> The next article will discuss another often-misunderstood concept: Context is not the context window—how an Agent Runtime should unify management of capabilities, permissions, and isolation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Data Bricklaying Diary
Records practices, thoughts, and pitfalls on the data grunt-work journey, sharing content on data platforms, data analysis, data processing, data governance, knowledge graphs, and more. Less theory, more hands‑on, making complex data technologies simple.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
