Skip to content

feat: Unified module storage, polyglot dispatch, app-layer services, WASM hook registration - #45

Merged
Brian Krabach (bkrabach) merged 62 commits into
mainfrom
feat/unified-module-storage
Mar 11, 2026
Merged

feat: Unified module storage, polyglot dispatch, app-layer services, WASM hook registration#45
Brian Krabach (bkrabach) merged 62 commits into
mainfrom
feat/unified-module-storage

Conversation

@bkrabach

@bkrabach Brian Krabach (bkrabach) commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three design phases implemented, enabling non-Python hosts to provide app-layer services and making WASM/gRPC modules loadable from bundle config:

Phase 1: Unified Module Storage & Arc Sharing (design: 2026-03-09)

  • Arc<HookRegistry> in Coordinator — enables shared ownership for binding layers
  • Node bindings fixed — getters share real Arc<Coordinator> and Arc<HookRegistry> (deleted disconnected-copy factory methods)
  • gRPC transport symmetry — added load_grpc_provider, load_grpc_hook, load_grpc_context, load_grpc_approval
  • Feature gate decouplingLoadedModule and resolve_module() available without --features wasm
  • Doc-tests on all new public APIs
  • Zero warnings — all 39 unused_must_use in test code suppressed

Phase 2: Session Init Polyglot Dispatch (design: 2026-03-09)

  • Session init deduplicatedsession.py:initialize() delegates to _session_init.initialize_session(), eliminating ~200 lines of duplication
  • Transport dispatch in loader.py — Rust resolve_module() detects transport after source resolution, branches to WASM/gRPC/Python
  • loader_dispatch.py deleted (131 lines) — was at wrong abstraction boundary, duplicated Rust logic
  • 5 new PyWasm* wrappers — Provider, Hook, Context, Orchestrator, Approval for all 6 WASM module types
  • WASM integration tests for all 6 types (marked @pytest.mark.slow for ARM64)

Phase 3: App-Layer Services & Review Fixes (design: 2026-03-10)

  • ApprovalProvider wired to Coordinator — Rust trait field + PyApprovalProviderBridge wrapping Python ApprovalSystem
  • DisplayService trait added — new Rust trait + Coordinator field + PyDisplayServiceBridge
  • WASM hook get-subscriptions — new WIT export lets hooks self-describe event subscriptions; host registers at mount time (fixes silent hook drop bug)
  • gRPC GetSubscriptions RPC added to HookService proto
  • Engine errors promoted to warning (was debug)
  • PyWasmOrchestrator documentation improved with mount warnings
  • _safe_exception_str deduplicated

Design docs

  • docs/plans/2026-03-09-unified-module-storage-design.md
  • docs/plans/2026-03-09-session-init-polyglot-dispatch-design.md
  • docs/plans/2026-03-10-app-layer-services-design.md

Python backward compatibility

Zero breaking changes. All existing Python APIs preserved unchanged. 514 Python tests pass (up from 495 at session start).

Test counts

Suite Count Status
Rust unit tests 448 ✅ PASS
Rust doc-tests 19 ✅ PASS
Rust integration tests 12 ✅ PASS
Node.js Vitest 68 ✅ PASS
Python pytest 514 passed, 1 skipped ✅ PASS
WASM integration (slow) 6 ⏳ Deselected on ARM64
Clippy Clean
Rustfmt Clean
Compiler warnings Zero

🤖 Generated with Amplifier

Co-Authored-By: Amplifier 240397093+microsoft-amplifier@users.noreply.github.com

Brian Krabach (bkrabach) and others added 30 commits March 9, 2026 18:12
Design for making Rust the single source of truth for module storage
across non-Python transports. Five components:

1. Arc<HookRegistry> in Coordinator for shared ownership
2. Fix Node bindings to share real Arc instances
3. Complete gRPC transport symmetry (4 missing load functions)
4. Decouple LoadedModule from wasm feature gate
5. Documentation via docstrings, not prose examples

Establishes Python-from-Rust-host pattern via gRPC adapters.
Preserves 100% Python backward compatibility.
- Add `let _ =` before 39 unused .register() return values in tests
  (hooks.rs: 31, grpc_server.rs: 4, session.rs: 3, coordinator.rs: 1)
- Auto-fix 2 `variable does not need to be mutable` warnings in
  bindings/python/src/lib.rs via `cargo fix --lib -p amplifier-core-py`
Approved design for wiring WASM and gRPC module loading into the
Python host's session init path. Key decisions:

- Deduplicate session.py/session_init.py (~200 lines removed)
- Absorb transport dispatch into loader.py after source resolution
- Delete loader_dispatch.py (wrong abstraction boundary)
- Extend load_and_mount_wasm() for all 6 module types
- Rust resolve_module() as single source of truth for transport detection
Add 4 tests verifying AmplifierSession.initialize() delegates to _session_init.initialize_session(). Tests intentionally fail with AttributeError because session.py does not yet import initialize_session - this is the RED phase of TDD.
RED test: test_wasm_dispatch_returns_mount_function verifies that
loader.load() can dispatch to WASM transport and return a callable
mount function. Currently fails with TypeError because loader.load()
does not yet accept a coordinator parameter and has no WASM dispatch
branch.

Task-3 of unified-module-storage implementation plan.
The code quality reviewer suggested extracting the string literal "echo-tool" (repeated 6 times) to a module-level constant MODULE_ID for clarity. No behavioral change.
- Add coordinator parameter to load() for polyglot dispatch
- Insert transport dispatch block before Python validation: routes
  to WASM or gRPC loaders when Rust engine resolves non-Python transport
- Add _make_wasm_mount() and _make_grpc_mount() helper methods
- Pass coordinator=coordinator at all 5 loader.load() call sites
  in _session_init.py (orchestrator, context, providers, tools, hooks)
- Update test mocks in test_multi_instance.py to accept new parameter

🤖 Generated with Amplifier

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
- Deleted python/amplifier_core/loader_dispatch.py: functions (_detect_transport,
  _read_module_meta, load_module) are now absorbed into loader.load()
- Deleted bindings/python/tests/test_loader_dispatch.py: tested internal
  implementation details of loader_dispatch.py
- Deleted bindings/python/tests/test_dispatch_integration.py: tested internal
  implementation details of loader_dispatch.py
- Rewrote python/tests/test_loader_dispatch_wasm.py: new test exercises WASM
  mounting through loader.load() dispatch path instead of old
  loader_dispatch.load_module()
- Updated tests/validate_rust_kernel.py: replaced loader_dispatch import check
  with loader.ModuleLoader import check

All transport and module dispatch logic is now unified through loader.load(),
reducing code duplication and improving test maintainability.

🤖 Generated with Amplifier

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
- Add get-subscriptions function to hook-handler interface in WIT
- Add event-subscription record type to WIT
- Add EventSubscription struct to guest SDK types.rs
- Add get_subscriptions method to HookHandler trait
- Update export_hook! macro to export get_subscriptions WASM function
- Update test implementations to include get_subscriptions method

All 94 guest SDK tests pass with new functionality.
@bkrabach Brian Krabach (bkrabach) changed the title feat: Unified Module Storage — Arc sharing fix, gRPC transport symmetry, feature gate decoupling feat: Unified module storage, polyglot dispatch, app-layer services, WASM hook registration Mar 10, 2026
…for old modules

- Add WasmHookBridge::subscriptions_from_result() (pub(crate)) to classify
  errors: 'not found' → debug log + wildcard, other → warn log + wildcard.
  Mirrors the GrpcHookBridge pattern for UNIMPLEMENTED fallback.

- Change WasmHookBridge::get_subscriptions() from WasmResult<Vec<...>> to
  Vec<...> (infallible), applying graceful fallback internally so callers
  never need to handle a missing-export error.

- Add get_subscriptions() default method to HookHandler trait (returns
  wildcard), overridden by WasmHookBridge to call the WASM export.  This
  allows Arc<dyn HookHandler> callers to query subscriptions without
  downcasting.

- Replace hardcoded wildcard vec in bindings/python/src/lib.rs mount path
  with hook.get_subscriptions(&config), so modules with the export use
  their declared subscriptions; old modules fall back to wildcard.

- Add future-bidirectional-path comment to wit/amplifier-modules.wit near
  get-subscriptions describing the register-hook host import approach.

- Fix pre-existing rustfmt issue in grpc_hook.rs (long parameter line).
@bkrabach
Brian Krabach (bkrabach) merged commit 55021f4 into main Mar 11, 2026
4 of 8 checks passed
@bkrabach
Brian Krabach (bkrabach) deleted the feat/unified-module-storage branch March 11, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant