Repository navigation
[AAASM-6156] ✅ (examples): Patch adapter discovery instead of a private SDK helper - #614
Merged
Chisanan232 merged 3 commits intoSep 23, 2026
Merged
Conversation
All 16 Python framework examples patched the SDK's private
`agent_assembly.core.assembly._register_adapters` with `return_value=[]` so
`init_assembly(mode="sdk-only")` could run with no adapters installed. That
helper's return shape is not a contract, and in SDK 0.0.1rc7 it became a
2-tuple, so one dependency bump broke all 16 at once.
They now patch `AdapterRegistry.get_available_adapters_by_priority`, a
documented seam whose contract — the available adapters, in priority order —
does not move with the SDK's internals. `_register_adapters` constructs its
own `AdapterRegistry`, so the patch still yields zero adapters.
The `_start_network_layer` patch is dropped rather than ported: under
`mode="sdk-only"` the real function returns `("sdk-only", <no-op>)` with no
side effects, which is exactly what the mock returned, so it only pinned a
second internal shape.
Every assertion is unchanged — same `agent_id`, and the seven files that
assert `ctx.network_mode == "sdk-only"` still do. No example imports
`agent_assembly.core` any more.
Refs AAASM-6156
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ples AAASM-6156 was one stale mock of a private helper copied into 16 directories. Nothing prevented it, and nothing prevents the next one: a private helper's signature and return shape carry no contract, so they can move again. `scripts/check_example_sdk_mocks.py` parses every Python example test with `ast` and reports: - EX-MOCK-01 — patching a private attribute of an `agent_assembly` object; - EX-MOCK-02 — reaching a public attribute through a private module; - EX-MOCK-03 — one example's set of seams differing from its siblings', which is the half-applied update that would leave the same defect mid-flight; - EX-MOCK-04 — a computed attribute name, which the gate cannot classify and so refuses to pass silently. It resolves patch targets through import aliases, so the aliased `mock_patch` and the combined `with (a, b):` forms are seen; a text-level check missed both. Zero discovered files and an unparseable file both exit non-zero rather than reporting a clean tree the gate never read. `scripts/test_check_example_sdk_mocks.py` keeps it falsifiable: its fixtures are the real pre-fix and post-fix text, and it asserts the gate separates them. It also records that EX-MOCK-03 is silent on the original defect — all 16 examples agreed with each other — so EX-MOCK-01 is the rule that catches it. Refs AAASM-6156 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Runs the checker's own unit tests before the checker, matching `dependabot-coverage.yml`: a parser that silently stops matching would report success on a tree it did not understand. No `paths:` filter. The gate's discovery globs are part of what it asserts, so a filter keyed on today's example layout would skip exactly the change that invalidates it. Stdlib-only, ~32 small files, well under a second. Actions are pinned by SHA to the versions already used across this repo's workflows. Protection is untouched; `main` lists zero required checks, so this reports without blocking (AAASM-5677). Refs AAASM-6156 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Chisanan232
deleted the
v0.0.1/AAASM-6156/fix/example_sdk_mock_contract
branch
September 23, 2026 04:44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What changed
All 16 Python framework examples patched a private SDK helper in their smoke test so
init_assembly(mode="sdk-only")would run with no adapters installed:They now patch a documented public seam instead, and drop the second patch entirely:
Plus a guard so this class of coupling cannot come back:
scripts/check_example_sdk_mocks.py,its unit tests, and a workflow that runs both.
Why it changed
_register_adaptersis private, so its return shape is not a contract. In SDK0.0.1rc7itbecame a 2-tuple of the adapters plus an audit-sink disposition. Because the old shape had been
copied into 16 directories, a single dependency bump turned every Python example red at once,
each reporting
— a message about a mock, for a change that broke nothing documented.
Why the seam, and not the tuple
Correcting the mock to
return_value=([], "absent")was the obvious fix and it is the wrong one.That shape can only ever be written against rc.7, so it could not land here:
mainpins rc.6,where
agent_assembly.core.audit_sinkdoes not exist at all. Measured inpython:3.12-slim,each run printing its preconditions (
native_core_available=True,connect_runtime_client=OBJECT)so only the SDK and the mock differ:
_register_adapters→[]— what the 16 directories didPASS, with the AAASM-4547 unregistered warning_register_adapters→([], AUDIT_SINK_ABSENT)RAISED ModuleNotFoundError: No module named 'agent_assembly.core.audit_sink'AdapterRegistry.get_available_adapters_by_priority→[]— this changePASS, with the same warning_register_adapters→[]RAISED ConfigurationError: ... gateway gRPC endpoint is unreachable for registration_register_adapters→([], AUDIT_SINK_ABSENT)RAISED ConfigurationError(same)AdapterRegistry...→[]RAISED ConfigurationError(same)Two things follow. The tuple correction is not viable on
main, so the "fix the shape" plan wouldhave forced this change to travel on the rc.7 adoption branch and stay unverifiable until rc.8
exists. The seam correction passes on rc.6 today, so it lands here, independently green, and
reduces #613 to a version bump plus a
relock.
The bottom three rows are also the honest statement of what this change does not fix: released
rc.7 raises regardless of the mock, because that is
AAASM-6155, fixed separately in
python-sdk#343 and not yet released.
Why
AdapterRegistry.get_available_adapters_by_priorityis the right seamdocs/concepts/index.md; its contract is "the available adapters,in priority order", which does not move when the SDK reshuffles its internals.
_register_adaptersconstructs its ownAdapterRegistry(), so patching the class method stillyields zero adapters — the real helper runs, builds an interceptor, resolves the audit sink and
returns whatever it returns. The test never asserts that value, which is the point.
Why
_start_network_layeris dropped rather than portedUnder
mode="sdk-only"the real function is alreadyif mode == "sdk-only": return "sdk-only", _noop_shutdown— no side effects, and byte-for-bytewhat the mock returned. Patching it bought nothing and pinned a second internal shape.
Assertions are unchanged
Every
agent_id, everyinit_assemblyargument and every assertion is preserved. Normalising the16 rewritten test bodies on their three genuine per-example axes collapses them to one identical
body, which is the check that no file drifted during the edit:
patchinside the function, 8 at module level;ctx.network_mode == "sdk-only", 9 do not (as before);haystack-tool-policybinds it asmock_patch(as before).agent_idnetwork_modepatchbound asagno-tool-policytest-agno-agentpatchautogen-tool-policytest-autogen-agentpatchcrewai-research-crewtest-crewpatchcustom-tool-policytest-custom-agentpatchgoogle-adktest-google-adk-agentpatchhaystack-tool-policytest-haystack-agentmock_patchlangchain-basic-agenttest-langchain-agentpatchlangchain-research-agenttest-research-agentpatchlanggraphtest-langgraph-agentpatchllamaindex-tool-policytest-llamaindex-agentpatchmicrosoft-agent-framework-tool-policytest-maf-agentpatchopenai-agents-sdktest-openai-agentpatchpydantic-aitest-pydantic-ai-agentpatchsemantic-kernel-tool-policytest-semantic-kernel-agentpatchsmolagents-tool-policytest-smolagents-agentpatchstrands-agents-tool-policytest-strands-agentpatchNo example imports
agent_assembly.coreany more. The two scenario examples(
scenarios/{approval-gates,policy-enforcement}/python) patch nothing and are untouched.The guard
scripts/check_example_sdk_mocks.pyparses every Python example test withastand reports fourrules: patching a private
agent_assemblyattribute (EX-MOCK-01), reaching a public attributethrough a private module (
EX-MOCK-02), one example's set of seams differing from its siblings'(
EX-MOCK-03— the half-applied update that leaves the same defect mid-flight), and a computedattribute name it cannot classify and so refuses to pass silently (
EX-MOCK-04).It resolves patch targets through import aliases, so the aliased
mock_patchand the combinedwith (a, b):forms are both seen — a text-level check missed both, which is why this is AST-based.Zero discovered files and an unparseable file each exit non-zero rather than reporting a clean tree
the gate never read.
How to verify
The guard is falsifiable against the real defect
Its fixtures are the real pre-fix and post-fix text, not paraphrases. Run against the two trees:
032 file(s) scanned, zero private-seam patches, one seam in usepython/reverted to pre-fix (git stash push -- python/)1EX-MOCK-01Worth stating plainly:
EX-MOCK-03reports nothing on the original defect, because all 16examples agreed with each other — they were uniformly wrong.
EX-MOCK-01is the rule that catchesAAASM-6156;
EX-MOCK-03catches the next one, where 15 directories get updated and one is missed.That is asserted in the checker's tests so a reader cannot mistake which rule does which job.
The rewritten tests pass on the rc.6 that
mainpinspython:3.12-slim,agent-assembly==0.0.1rc6, runningpytest tests/test_smoke.py -k sdk_only.The Environment column records what was actually installed per example, because it differs:
-k sdk_onlyresultagno-tool-policypyproject.tomldeps installed1 passedautogen-tool-policypyproject.tomldeps installed1 passedcrewai-research-crew1 passedcustom-tool-policy1 passedgoogle-adkpyproject.tomldeps installed1 passedhaystack-tool-policypyproject.tomldeps installed1 passedlangchain-basic-agent1 passedlangchain-research-agent1 passedlanggraph1 passedllamaindex-tool-policypyproject.tomldeps installed1 passedmicrosoft-agent-framework-tool-policy1 passedopenai-agents-sdk1 passedpydantic-ai1 passedsemantic-kernel-tool-policypyproject.tomldeps installed1 passedsmolagents-tool-policypyproject.tomldeps installed1 passedstrands-agents-tool-policypyproject.tomldeps installed1 passed16 of 16 pass. Eight needed only the SDK and pytest; the other eight were inconclusive in that environment (
ModuleNotFoundErrorfor their framework, or a module-levelpytest.importorskip) and were re-run with each example's own dependencies installed, where all eight pass.The patched seam is on the real code path, not a decorative no-op. Probing
langgraphon rc.6 with its framework installed:init_assembly(mode="sdk-only")calls the seam exactly once, and unpatched discovery there returns one adapter — soreturn_value=[]genuinely changes what the code under test sees.One limitation worth naming: rc.6 green is a no-regression result, not proof the change fixes anything. Row 1 of the table above shows the old mock also passes on rc.6. What this change buys is that the same tests will survive the SDK's next internal reshuffle — which is what the guard, and the rc.7 rows, are the evidence for.
Local caveat, stated rather than hidden:
uv sync --extra dev --locked— what CI runs — could notcomplete against the configured registry on this workstation, so the runs above installed each
example with
pip install .in a container instead. CI runs the canonical locked environment, andVerify Python Examplesis the authoritative check on this pull request.Scope
This does not adopt rc.7 and does not touch any lockfile or version pin.
#613 stays draft; once this is on
mainitreduces to bumping
python.versioninmetadata/sdk-versions.yaml, regenerating the per-directorymetadata, and relocking — and it still needs an SDK release containing the AAASM-6155 fix before it
can be revalidated.
Still worth doing separately, not here: the SDK has no public seam for "run
init_assemblywith noadapters and no gateway". This change moves the examples onto the most stable public seam that
exists today, which is an improvement, not a substitute for that.
Closes AAASM-6156
External evidence is text only, per this workstation's data-handling policy.