[AAASM-1846] ✨ (core): Add gateway URL / api_key resolver + local auto-start#58
Merged
Chisanan232 merged 19 commits intoMay 23, 2026
Merged
Conversation
Lays down the new module that will host the zero-config resolution logic for init_assembly (AAASM-1846 / E17 S-G). Defines the default gateway URL, healthz path, probe / auto-start timeouts, env-var names, and the auto-start argv tuple. No behavior yet.
Synchronous httpx GET against ``{base_url}/healthz`` with a tight
default 500ms timeout. Any HTTPError is swallowed (timeout, connection
refused, DNS failure) and surfaces as False — the resolver treats
unreachable as "absent" rather than fatal.
Covers the three expected outcomes: 2xx → True (and verifies the ``/healthz`` suffix), httpx exception → False, non-2xx → False (parametrized across 400/404/500/503). All httpx calls mocked — no network.
Polls the gateway healthz endpoint until success or timeout. Used after ``_auto_start_gateway`` to know when the freshly-spawned local CP is ready to accept connections. Default 5s budget per Story AC; final re-probe after the deadline ensures borderline races resolve cleanly.
Three behaviors: success on first probe (no sleep), success after two prior failures (verifies the poll-then-sleep loop body), and false when the timeout elapses with no success. All time.sleep calls patched so the test stays under 50ms.
Reads ~/.aasm/config.yaml when present. PyYAML is treated as a soft dependency — missing import returns an empty dict so the resolver falls through to the local-default step. File-missing, OS errors, parse errors, and non-mapping payloads all collapse to the same empty result — config-file lookup is purely advisory.
Covers four behaviors: missing file → {}, well-formed YAML →
parsed mapping, non-mapping root (e.g. top-level list) → {},
and yaml-module-absent → {} (simulated by stubbing sys.modules).
Uses tmp_path; no real ~/.aasm/ touched.
Spawns ``aasm start --mode local --foreground`` as a detached subprocess and waits for /healthz to become ready. Raises ConfigurationError when the aasm binary is missing from PATH and GatewayError when the spawned gateway doesn't come up within the timeout. The detached start_new_session=True is the docker-daemon-style hand-off that lets the gateway survive after the calling Python process exits.
Three behaviors: aasm-missing → ConfigurationError with install hint, spawn succeeds + healthz ready → returns None (also pins the argv and start_new_session=True for the detach contract), spawn succeeds but timeout elapses → GatewayError. All subprocess and HTTP calls patched.
Implements the 4-step precedence chain from Epic 17 S-G: explicit kwarg → AAASM_GATEWAY_URL env var → ~/.aasm/config.yaml agent.gateway_url → local default (probe + auto-start). Steps 1-3 short-circuit; step 4 may spawn ``aasm`` and bubble ConfigurationError/GatewayError when the local gateway is unavailable and cannot be brought up.
Five tests exercising the 4-step precedence chain: explicit > env > config > local-default; the local-default branch is split into probe-hit (no auto-start) and probe-miss (auto-start invoked with the canonical localhost URL). monkeypatch handles env-var isolation so tests stay parallel-safe.
Mirrors resolve_gateway_url's 4-step precedence chain for api_key: explicit kwarg → AAASM_API_KEY env → config file → empty default. No auto-start path — local mode is unauth-accepting per the Epic, so the empty fallback is the documented default rather than an error.
Four tests mirroring resolve_gateway_url's chain: explicit > env > config > empty-default. No raise on missing — empty string is the documented local-mode default.
Relaxes init_assembly to accept None for gateway_url and api_key and calls resolve_gateway_url / resolve_api_key to fill them in via the 4-step precedence chain (explicit → env → config → local default + auto-start). _validate_inputs no longer rejects empty api_key — local mode is unauth-accepting per Epic 17. Existing callers that pass a real URL + key are unaffected.
Three small fixes so pre-commit mypy passes on the new module: explicit int annotation on httpx response.status_code to avoid Any return, multi-code type: ignore on the soft yaml import (covers both the import-untyped at runtime and the unused-ignore when stubs are present in pre-commit's isolated mypy env), and string-form patch() in the tests to avoid attr-defined complaints on module-level imports.
Empty gateway_url and empty api_key are no longer hard errors — they now route through the resolver chain (env → config file → local default) per AAASM-1846. Only the truly invalid mode case still raises ConfigurationError immediately. The zero-config and explicit-args paths are covered by separate new tests.
Exercises the Story AAASM-1846 primary AC: init_assembly() with no arguments and no env vars connects to http://localhost:7391 and uses empty api_key. Probes are patched so the test doesn't require a real local gateway or aasm binary on PATH.
Story AC: existing callers passing both gateway_url and api_key must be unaffected by the resolver path. Patches _probe_healthz and _auto_start_gateway with sentinels that raise if invoked — proves the resolver short-circuits on explicit args and the client binds verbatim.
Empty gateway_url and empty api_key are now resolver-handled, so they no longer raise ConfigurationError directly — same behavior shift as the unit test. Keeping only the unknown-mode case, which remains an immediate ConfigurationError. Fixes integration suite regression from this branch and prevents the leaked _ACTIVE_CONTEXT chain that cascaded into the topology-registration tests.
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This was referenced May 22, 2026
Contributor
Author
🤖 Claude Code review record — AAASM-1846 ST-1Reviewer: Claude Code (Opus 4.7, 1M context) CI status
All 22 checks green. No failing gates. Scope coverage vs ticketCross-checked PR diff against AAASM-1846 description:
Acceptance criteria coverage
End-to-end smoke (run 2026-05-23 against
|
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.



Description
Add zero-config gateway resolution to the Python SDK so
init_assembly()with no arguments connects to a local gateway athttp://localhost:7391, auto-startingaasm start --mode local --foregroundif nothing is listening.Implements ST-1 (AAASM-1846) of Story AAASM-1581 (E17 S-G — SDK auto-detect across Python / Node / Go) under Epic AAASM-1568.
Resolution order
init_assembly(gateway_url="...", api_key="..."))AAASM_GATEWAY_URL,AAASM_API_KEY)~/.aasm/config.yaml, soft pyyaml dependency)http://localhost:7391/healthz; if unreachable, spawnaasm start --mode local --foreground(detached viastart_new_session=True) and wait up to 5sFiles
agent_assembly/core/gateway_resolver.py—_probe_healthz,_wait_for_healthz,_load_config_file,_auto_start_gateway, publicresolve_gateway_url/resolve_api_keyagent_assembly/core/assembly.py—init_assembly()makesgateway_urlandapi_keykeyword-only optional, wires resolvers;_validate_inputsno longer rejects empty api_key (local mode is unauth-accepting per Epic 17)test/unit/core/test_gateway_resolver.py— 25 unit teststest/unit/test_assembly.py— adds zero-arg + explicit-args regression tests; drops empty-string-raises assertionstest/integration/test_assembly_integration.py— same alignmentType of Change
Breaking Changes
init_assembly(gateway_url, api_key, ...)— both arguments are now optional, defaulting toNone. Callers that explicitly passed empty strings (gateway_url=""/api_key="") now get resolver fallback behavior instead of an immediateConfigurationError. Callers passing real values are unaffected (covered by a dedicated regression test).Related Issues
Testing
test/unit/core/test_gateway_resolver.py)test/unit/test_assembly.py— zero-arg + explicit-args regression)All subprocess and HTTP calls are mocked in unit tests — no real
aasmspawn, no real network. Healthz probe / auto-start timing patched (time.sleeppatched too) so the resolver test suite runs in <50ms total.Checklist
try: import yaml)