grid_exploration
The ext.leonard.* VM-service host — the layer that makes a running
the_grid station debuggable from outside.
grid_exploration registers the exploration wire protocol over a
GridControllerRuntime via dart:developer service extensions — pure Dart, no
Flutter binding, no bespoke ext.grid.* namespace. An
external debugging harness (a stock leonard) attaches to the station's VM
service URI, handshakes, discovers the grid namespace, and reads live graph
state.
The surface
ext.leonard.core.handshake— protocol version'2', host identity, and theextensionsmanifest ([{namespace: 'grid', tools: [...]}]). The handshake and the registrar read the SAME tool list, so a registered tool is always a discoverable tool.ext.leonard.core.get_stable_observation— empty semantics/routes, sync-loop stability, and the grid fragment underextensions.grid: read path, bead/ready counts, ready-bead summaries, recent events, stats.ext.leonard.grid.{requery,snapshot,ready,events,stats}— the closed, read-only tool set. Every dispatch returns an{ok, value|error}envelope; a thrown error becomes aServiceExtensionResponse.error, never a silent drop.ext.leonard.grid.reload— dev-mode only. It exists only when a JIT station under--enable-vm-servicecomposes aReassembleTool(the station'shotReload/hotRestartwired in as plain callbacks, so this package never depends ongrid_sdk); an AOT composition passes none and the host is exactly the read-only five. The host REFUSES at construction on a tool-name collision.- The event stream — every typed
GraphEventis posted viadeveloper.postEvent('grid.controller.event', ...)in a compact wire shape (graphEventToWire, an exhaustive switch over the sealed union, so a new event variant forces a wire decision).
GridExplorationHost splits into pure JSON builders (handshakeJson /
observationJson / dispatchTool — unit-tested with no VM service) and a thin
idempotent register() that binds them to dart:developer.
"extension", never "plugin"
The seam word is extension. The wire key is extensions in both the
handshake and the stable observation, with no plugins fallback — readers
at ≥0.1.0 read only extensions (the ext.leonard.* prefix, method names,
and protocol version are unchanged). Keep new code, docs,
and wire fields in extension vocabulary; one legacy class name still carries
the old word — its rename is tracked separately; do not add more.
Testing — pinned conformance fixtures
Three rings, hermetic to fully live:
- Offline fixture replay (
test/conformance_fixture_test.dart). The pinned bytes infixtures/exploration/2026-06-15-leonard-extensions/(repo root) are decoded through a leonard-faithful reader — ONLYextensions, no fallback — and re-produced by today's builders (normalized for the volatilelastRefreshMs), so any wire drift — a reintroducedpluginskey, a renamed field, a dropped tool — fails offline. Re-capture viatool/capture_conformance_fixture.dart; never hand-edit the fixtures. - In-process attach (
test/attach_conformance_test.dart,test/vm_service_attach_test.dart). A real VM-service client attaches to the test's own process and exercises handshake → observation → a grid tool call. Rundart test --enable-vm-service -t integration; self-skips when the VM service is absent. - Cross-process, real lenny (
test/leonard_drive_attach_test.dart,test/leonard_cli_attach_test.dart).tool/attach_target.dartboots a real host under its own VM service; lenny's credential-freeleonard_drivedriver (strong assertions, zero model calls) and the fullleonard_cliagent loop attach over ws://. Both self-skip when lenny is not discoverable (the CLI also when inference credentials are unarmed), so the offline suite (dart test -x integration) stays hermetic.
Libraries
- grid_exploration
- Exploration-protocol host for the_grid.