Parent epic: #929
Design proposal: PR #1080
Phase: 1 (Wave 2 — start after 0.1 has a draft PR)
Depends on: 0.1
Blocks: 1.B, 1.C, 1.D, 2.1, 4.1
⚠️ Phase 1 bottleneck. This is the only serialisation point before Wave 3 opens four parallel tracks. Keep the PR narrow (~300–500 LOC) to merge quickly.
Problem
mellea/stdlib/components/intrinsic/_util.py:call_intrinsic and mellea/stdlib/requirements/requirement.py (requirement rerouting) branch on the old IntrinsicAdapter / EmbeddedIntrinsicAdapter subclasses. Until these internal callers operate on the new Adapter type from 0.1, no helper file can migrate. External users may also construct the old classes directly — migrating internals without a backward-compat path breaks them.
Agreed design
Two tightly-coupled changes in one PR (splitting creates ordering pain and conflicting branch state):
(a) Old classes become inheriting shims
IntrinsicAdapter, EmbeddedIntrinsicAdapter, CustomIntrinsicAdapter are restructured to:
- Inherit from
Adapter (new dataclass from 0.1) — isinstance(x, IntrinsicAdapter) and isinstance(x, Adapter) both work
- Translate constructor args into
Identity + IOContract + WeightsBinding triple, then call Adapter.__init__
- Emit a
DeprecationWarning once per construction site (stacklevel=2) pointing at the new construction pattern
- Carry no behavioural state of their own — delegate to inherited
Adapter machinery
(b) Internal callers operate on Adapter
_util.call_intrinsic and requirement rerouting rewritten to:
adapter = backend.resolve_adapter(name)
with backend.adapter_scope(adapter):
raw = backend.generate(adapter.io_contract.build_prompt(...))
return adapter.io_contract.parse(raw)
adapter_scope wraps activate()/deactivate() per call; this is the future telemetry parent — Phase 2 wraps it in an intrinsic.call OTel span; the hook point must exist at the right call-site boundary now
prepare() at session start (2.2); release() at session teardown
- Role-based lookup for requirement rerouting uses
Identity.role instead of isinstance branching
- Backends grow stub
resolve_adapter / adapter_scope implementations delegating to old code paths (real implementations in Phase 2)
Docs update
docs/dev/requirement_aLoRA_rerouting.md — update to describe role-based lookup (using Identity.role) instead of hardcoded requirement-check string. This resolves thread 7 in the design proposal's thread mapping.
Scope
mellea/backends/adapters/__init__.py — old classes as inheriting shims
mellea/stdlib/components/intrinsic/_util.py — call_intrinsic rewritten
mellea/stdlib/requirements/requirement.py — rerouting rewritten
- Abstract backend + concrete backend stubs:
resolve_adapter, adapter_scope (no instrumentation yet)
docs/dev/requirement_aLoRA_rerouting.md — role-based lookup documentation
Out of scope
Helper file migration (1.B–D), backend verb implementations (Phase 2), AdapterMixin rename (2.1), shim removal (4.1).
Acceptance criteria
Test plan
test/backends/adapters/test_old_class_shims.py:
test_old_classes_inherit_from_adapter
test_old_constructor_emits_deprecation_warning — once per call, stacklevel=2
test_old_constructor_drop_in_replaceable — construct via old API, assert behaviour matches Adapter(...) directly
New tests for role-based lookup with multiple registered roles.
Breaking changes
- Subclassing the old classes: anyone overriding internal methods of
IntrinsicAdapter etc. may break. Public constructor signature preserved.
isinstance checks against the old Adapter ABC: depends on the naming-collision resolution chosen in 0.1.
Document both in the PR description with migration examples.
References
Parent epic: #929
Design proposal: PR #1080
Phase: 1 (Wave 2 — start after 0.1 has a draft PR)
Depends on: 0.1
Blocks: 1.B, 1.C, 1.D, 2.1, 4.1
Problem
mellea/stdlib/components/intrinsic/_util.py:call_intrinsicandmellea/stdlib/requirements/requirement.py(requirement rerouting) branch on the oldIntrinsicAdapter/EmbeddedIntrinsicAdaptersubclasses. Until these internal callers operate on the newAdaptertype from 0.1, no helper file can migrate. External users may also construct the old classes directly — migrating internals without a backward-compat path breaks them.Agreed design
Two tightly-coupled changes in one PR (splitting creates ordering pain and conflicting branch state):
(a) Old classes become inheriting shims
IntrinsicAdapter,EmbeddedIntrinsicAdapter,CustomIntrinsicAdapterare restructured to:Adapter(new dataclass from 0.1) —isinstance(x, IntrinsicAdapter)andisinstance(x, Adapter)both workIdentity+IOContract+WeightsBindingtriple, then callAdapter.__init__DeprecationWarningonce per construction site (stacklevel=2) pointing at the new construction patternAdaptermachinery(b) Internal callers operate on
Adapter_util.call_intrinsicand requirement rerouting rewritten to:adapter_scopewrapsactivate()/deactivate()per call; this is the future telemetry parent — Phase 2 wraps it in anintrinsic.callOTel span; the hook point must exist at the right call-site boundary nowprepare()at session start (2.2);release()at session teardownIdentity.roleinstead ofisinstancebranchingresolve_adapter/adapter_scopeimplementations delegating to old code paths (real implementations in Phase 2)Docs update
docs/dev/requirement_aLoRA_rerouting.md— update to describe role-based lookup (usingIdentity.role) instead of hardcodedrequirement-checkstring. This resolves thread 7 in the design proposal's thread mapping.Scope
mellea/backends/adapters/__init__.py— old classes as inheriting shimsmellea/stdlib/components/intrinsic/_util.py—call_intrinsicrewrittenmellea/stdlib/requirements/requirement.py— rerouting rewrittenresolve_adapter,adapter_scope(no instrumentation yet)docs/dev/requirement_aLoRA_rerouting.md— role-based lookup documentationOut of scope
Helper file migration (1.B–D), backend verb implementations (Phase 2),
AdapterMixinrename (2.1), shim removal (4.1).Acceptance criteria
IntrinsicAdapter(...)returns instance satisfying bothisinstance(x, IntrinsicAdapter)andisinstance(x, Adapter)EmbeddedIntrinsicAdapterandCustomIntrinsicAdapterDeprecationWarningper call (not per import),stacklevel=2_util.call_intrinsicoperates onAdapter; noisinstancebranching on old subclassesIdentity.roleadapter_scopeexists at the correct call-site boundary; pass-through context manager at this stagedocs/dev/requirement_aLoRA_rerouting.mdupdated; markdownlint passesruff format,ruff check,mypycleanTest plan
test/backends/adapters/test_old_class_shims.py:test_old_classes_inherit_from_adaptertest_old_constructor_emits_deprecation_warning— once per call,stacklevel=2test_old_constructor_drop_in_replaceable— construct via old API, assert behaviour matchesAdapter(...)directlyNew tests for role-based lookup with multiple registered roles.
Breaking changes
IntrinsicAdapteretc. may break. Public constructor signature preserved.isinstancechecks against the oldAdapterABC: depends on the naming-collision resolution chosen in 0.1.Document both in the PR description with migration examples.
References
resolve_adapter/adapter_scopeintroduced here