Repository navigation
feat: deploy + package manifest config spec implementation + system code update (atlas, pilot, executor; atlas API migration) - #27
Conversation
…deploy manifest
rust/contracts/ reorg to full-name layout:
- prm/ → primitive/{base,camera,sensor,robot}/
- srv/ → service/{slam,navigation,perception,map}/ and system/{,memory/,vlm/}/
- ids updated: robonix/prm/* → robonix/primitive/*, robonix/srv/* → robonix/service|system/*
- `kind` field flips to "system" for pilot / executor / liaison / memory / vlm
new primitive driver interface — lifecycle for hardware init/reset/shutdown/probe:
- shared IDL: lib/robonix_msg/srv/Driver.srv (uint8 command + config_json passthrough)
- 5 driver contracts: base/driver, camera/driver, sensor/{imu,lidar,lidar3d}_driver
rbnx CLI — new per-package manifest + deploy manifest:
- package_manifest.yaml replaces robonix_manifest.yaml inside packages (old name still read)
- top-level `start` + `build` shell strings (no more `nodes: [...]`); `-n/--node` dropped
- `capabilities: [{name, path?}]` — path points to package-local TOML (skill case)
- `package.name` canonical (package.id accepted as alias)
- new `rbnx deploy -f robonix_manifest.yaml` brings up a whole stack
- deploy manifest: system:/primitive:/service:/skill: blocks, each with config: passed
to the package via RBNX_CAP_CONFIG_JSON
- url:/branch: support for remote packages (mapping/nav live in separate repos)
- backward-compat: old manifests (robonix_manifest.yaml with nodes: + package.id +
build.script:) still load, emit a one-line deprecation warning
executor MCP tool discovery — one interface = one tool:
- was: single mcp_tools interface bundled tools[] in metadata
- now: each tool gets its own DeclareInterface with its own contract_id
(`robonix/skill/<ns>/<tool>`), executor reads name from iface.name
docs:
- docs/src/integration-guide/packaging-spec.md — spec writeup covering:
deploy manifest, package manifest, config passthrough, multi-instance
(primitive device binding), driver lifecycle, official (primitive/service/system)
vs user-defined (skill) capabilities, rbnx codegen usage, full primitive
and skill package examples
… deploy manifest
- packages/{vlm_service,memsearch_service,clawhub_skills,tiago_sim_stack}/
get new package_manifest.yaml with top-level start + build shell strings
and capabilities: declarations. Old robonix_manifest.yaml files kept for
backward-compat (rbnx reads either; the old-format loader emits a
deprecation warning). Multi-node packages (maniskill_vla_demo,
zero_copy_demo) stay on the legacy format until they're split into
one-process-per-package units.
- examples/robonix_manifest.yaml — top-level deploy manifest that
`rbnx deploy` consumes. Lists system services (atlas/executor/pilot/
liaison/memory/vlm) + every scene/skill package with its config. One
command brings up the full stack; no more examples/run.sh required.
rbnx deploy now writes each instance's config to `rbnx-deploy/instances/<name>.json` and hands the package two env vars: RBNX_CONFIG_FILE (absolute path) + RBNX_INSTANCE_NAME. Packages read the file via jq / json.load — no more bash-escaping the JSON into RBNX_CAP_CONFIG_JSON, no ARG_MAX risk on large configs, and `cat $RBNX_CONFIG_FILE` beats `printenv | jq` for debugging. Also remove rust/examples/packages/zero_copy_demo/ (retired demo) and its reference in examples/README.md + scripts/test_examples_packages.sh.
Robonix has no 'runtime' concept; the proto and the file inside
robonix-atlas were misleadingly named after one. Nothing semantic
changes — same gRPC surface, same trait, same RPCs.
- rust/proto/robonix_runtime.proto -> rust/proto/atlas.proto
- robonix-atlas/src/meta_runtime.rs -> robonix-atlas/src/service.rs
- proto package robonix.runtime -> robonix.atlas
- service RobonixRuntime { ... } -> service Atlas { ... }
- MetaRuntimeRegistry / MetaRuntimeService -> AtlasRegistry / AtlasService
- serve_meta_runtime() -> serve_atlas()
- InspectRuntime{Request,Response} RPC -> InspectAtlas{Request,Response}
- generated module pb::robonix_runtime_{server,client}::* renames
- docs submodule bump (parallel rename in robonix-book)
Follow-up commit will start migrating Node* messages → Cap* with the
old names marked deprecated, per dev-packaging spec.
Verified: `cargo check -p robonix-atlas` passes. Workspace-level check
is currently broken on this branch by unrelated in-flight deletions
of robonix-sdk / robonix-buffer / robonix-interfaces (tasks #31).
Dev-packaging spec migrates 'node' (legacy term) to 'cap' (capability instance). Cap-* schema and RPCs land in a follow-up commit; for now mark every Node-* message and Node-* RPC with `option deprecated = true` and add a migration note in the proto file. Behavior unchanged — proto3 deprecation is a marker that emits warnings in generated code without breaking wire compatibility. Existing clients keep working through the May release. Affected messages: RegisterNodeRequest / RegisterNodeResponse UnregisterNodeRequest / UnregisterNodeResponse NodeHeartbeatRequest / NodeHeartbeatResponse QueryNodesRequest / QueryNodesResponse NodeInfo Affected RPCs: RegisterNode, UnregisterNode, NodeHeartbeat, QueryNodes DeclareInterface and friends are not deprecated — they reference node_id as a string field, which the Cap-* migration will rename in place. Verified: cargo check -p robonix-atlas passes (workspace check is blocked by unrelated in-flight robonix-sdk/-buffer deletions).
…oncept
Old proto carried a lot of accumulated cruft that didn't survive
review:
- "node" terminology that never matched the spec; replaced with
"capability instance" (the running unit) + "capability interface"
(one contract_id binding under it). No deprecation tombstone — the
rename is intentionally hard.
- SkillInfo / QuerySkillMd referencing a SKILL.md file that does not
exist. Robonix uses CAPABILITY.md throughout; replaced with
CapabilityInfo / QueryCapabilityMd.
- DeclareInterface.supported_transports as repeated string. One
capability interface = one implementation, so each declaration
binds exactly one transport. To expose the same contract_id over
multiple transports, call DeclareInterface once per transport
(uniqueness is on (capability_id, contract_id, transport)).
- "channel" concept (NegotiateChannel / ReleaseChannel) that was
never actually defined or used end-to-end. Removed entirely.
Consumers discover endpoints via QueryCapabilities → InterfaceEndpoint
and connect directly.
- "provider" terminology with no formal definition. Gone — every
registered entity is a capability instance.
- distro / container_id / has_skill_md / kind fields — deployment
details that don't belong in the registry contract. Drop now;
reintroduce only with explicit need.
- SkillGraph TODO block (out-of-scope speculation).
New surface (rust/proto/atlas.proto, ~140 lines, was ~190):
service Atlas {
RegisterCapability(RegisterCapabilityRequest)
UnregisterCapability(UnregisterCapabilityRequest)
Heartbeat(HeartbeatRequest)
DeclareInterface(DeclareInterfaceRequest)
QueryCapabilities(QueryCapabilitiesRequest)
QueryCapabilityMd(QueryCapabilityMdRequest)
InspectAtlas(InspectAtlasRequest)
}
service.rs shrank from 1517 to ~340 lines — most of the cut is the
interface-catalog warning machinery, channel allocation, and per-RPC
validation duplication. Endpoint resolution per transport is in one
small helper; it picks a port from the registry's allocator for
"grpc", reads ros2_topic / shm_key from metadata_json for ros2 /
shared_memory, and falls back to caller-supplied "endpoint" for any
unknown transport.
end_to_end.rs is stubbed pending task #31 (robonix-sdk → robonix-proto
extraction); tests will be rewritten against the new Atlas API there.
Workspace check is still broken on this branch by other in-flight
deletions (sdk / buffer / interfaces). robonix-atlas itself compiles
cleanly in isolation:
cd rust && cargo check -p robonix-atlas
Two related cleanups in atlas.proto + service.rs:
(1) CapabilityInfo → CapabilityInterface, with the request/record field
renamed `capabilities` → `interfaces`. The struct describes ONE
capability interface (a single contract_id binding), not the whole
instance — its old name conflated the two.
(2) DeclareInterface no longer takes `uint32 listen_port`. Port numbers
are TCP-specific and don't belong on a transport-agnostic interface
declaration. Replaced with a `string endpoint` that the caller
proposes; Atlas resolves the actual stored endpoint per these rules
(documented inline in the proto):
empty proposal + atlas-mintable transport (ros2 / shared_memory)
-> Atlas mints "/rbnx/<contract>/<uuid8>"
empty + non-mintable transport (grpc, ...)
-> InvalidArgument
non-empty + no collision -> stored verbatim
non-empty + collision + mintable transport
-> Atlas appends "~<uuid8>"
non-empty + collision + non-mintable transport
-> AlreadyExists (caller rebinds and retries)
The response carries the AUTHORITATIVE endpoint (may differ from the
request when Atlas rewrote to disambiguate); caller MUST use the
returned value.
Drops:
- AtomicU16 port pool / `next_port()` (Atlas no longer allocates ports)
- AtlasService.data_plane_host (Atlas does not synthesize host:port)
- resolve_endpoint impl on AtlasService (now a freestanding fn over State)
- serve_atlas's data_plane_host arg
Atlas's responsibilities are now: storage, uniqueness checks, and name
minting for transports where minting is meaningful — nothing else.
Renamed env var ROBONIX_META_GRPC_ADDR → ROBONIX_ATLAS_LISTEN; dropped
ROBONIX_META_GRPC_ENDPOINT (no longer needed). Aligns with task #24.
Verified: `cargo check -p robonix-atlas` clean (workspace check is still
blocked by sdk/buffer/interfaces deletions in flight).
… of truth
Description text was duplicated between this struct and CAPABILITY.md,
which guarantees drift. CAPABILITY.md is the canonical doc; Atlas just
records the path. Prompt builders / VLM frontends read it directly.
message CapabilityInterface {
string name = 1;
string contract_id = 2;
- string description = 3;
string capability_md_path = 3; // was 4
string metadata_json = 4; // was 5
}
Field numbers compacted (no `reserved` marker — proto is still
pre-release on dev-packaging, no installed wire base to protect).
service.rs CapabilityInterfaceRec + From impls updated.
server_time_ms had three speculative use cases:
- clock skew detection between cap and Atlas
- eviction window planning by the cap
- timestamped liveness ack (already covered by ok)
None are real today: Atlas runs co-located, eviction threshold isn't
configurable nor exposed (so the timestamp can't be turned into "how
long do I have"), and ok already conveys the liveness ack. Drop now;
add back when there's a concrete user (e.g. distributed Atlas, exposed
eviction window).
Heartbeat response is now { ok }. RegisterCapabilityResponse stays as
{ capability_id } — that field is necessary when the request had an
empty capability_id and Atlas minted one, and the always-canonical
"use response.capability_id" pattern keeps callers branchless.
…md per package
Address pr-review-toolkit findings + user clarifications:
Schema (atlas.proto):
- New `Transport` enum (closed set: GRPC / ROS2 / SHARED_MEMORY / RAW_TCP /
WEBSOCKET). Replaces `string transport` everywhere. Adding a transport
is a deliberate proto bump, not a magic-string surprise.
- `capability_md_path` moved from `CapabilityInterface` to
`RegisterCapabilityRequest`: one CAPABILITY.md per package (per user
clarification), not per interface. Previously every interface carried
its own path which contradicts the spec.
- `QueryCapabilityMdResponse` simplified back to a single `string
capability_md` (no per-interface sectioning needed when there's one
doc per package).
- Removed dead `bool ok` from `HeartbeatResponse` and
`DeclareInterfaceResponse` (errors come back as gRPC Status; ok was
always true). `UnregisterCapabilityResponse.ok` renamed to
`was_present` since the bool there carries actual information.
- Re-numbered fields where types changed; this is a hard break,
intentional, no `reserved` markers (still pre-release).
Behavior (service.rs):
- `register_capability` now returns `AlreadyExists` on duplicate id
instead of silently overwriting and dropping endpoints.
- Namespace prefix violation now hard-rejects with `InvalidArgument`
(proto comment said "must"; honour the contract).
- `metadata_json` validated as parseable JSON at register and declare
time (rejects malformed payloads early).
- `query_capability_md` switched to `tokio::fs::read_to_string` (no
more sync I/O on the async runtime). Read errors propagate as
`Status::internal` with the path included instead of being swallowed.
- `inspect_atlas` propagates serde errors instead of masking them as
empty `{}`.
- `declare_interface` replaced `expect("checked above")` with
`ok_or_else(...)` defensive against future lock-splitting refactors.
- `MINT_ATTEMPTS` extracted as a const; mint-failure errors now include
contract_id, transport, and current cap count for debuggability.
- `Transport` -> string label via `serde(serialize_with = ...)` so
`inspect_atlas` JSON dump stays readable (`"ROS2"` not raw int).
- `parse_transport()` rejects `TRANSPORT_UNSPECIFIED` explicitly.
- `AtlasRegistry::require()` now returns `&str` instead of allocating a
fresh `String` on every call.
Doc-comments added on public items (`AtlasRegistry`, `AtlasService`,
`serve_atlas`).
docs submodule: bumped to syswonder/robonix-book main (now contains
all earlier `next` work; `next` branch deleted upstream).
Verified: cargo check -p robonix-atlas clean. Workspace check still
blocked by sdk/buffer/interfaces deletions in flight.
(1) Atlas proto — TRANSPORT_MCP added as a first-class transport. Most existing skill packages register against it (codegen turns a contract.toml into MCP Python types + @mcp.tool wrappers, and the package binds an HTTP MCP server). Marked non-mintable because the MCP server URL is caller-bound. (2) Documentation — drop legacy "SKILL.md" / "skills/" wording from rust/README.md and rust/crates/robonix-pilot/README.md, in favour of the single CAPABILITY.md-per-package model: - Key-concepts section rewritten around "capability instance" / "capability interface" / "Transport" / "CAPABILITY.md" — matches the vocabulary block at the top of atlas.proto. - Removed mention of the now-deleted "channel" / NegotiateChannel. - Pilot README's system-prompt step now says "each registered package's CAPABILITY.md" instead of "SKILL.md index". Pilot and CLI source still reference `skill_md`/`SKILL.md` because they import the retired robonix-sdk and won't compile anyway; they'll be rewritten against the new Atlas API as part of task #31. Likewise for the example services (vlm_service, memsearch_service, tiago_bridge).
Final pass on the metadata_json escape hatch. Two changes:
(1) DeclareInterfaceRequest / InterfaceEndpoint — replace
`string metadata_json` with a discriminated `TransportParams` oneof:
message TransportParams {
oneof kind {
GrpcParams grpc = 1;
Ros2Params ros2 = 2;
SharedMemoryParams shared_memory = 3;
RawTcpParams raw_tcp = 4;
WebsocketParams websocket = 5;
McpParams mcp = 6;
}
}
Each Params message carries the fields actually used in the codebase
today (surveyed across vlm_service, memsearch_service, tiago_bridge,
plus the deprecated maniskill demo for MCP shape):
- GrpcParams: protobuf service identity (proto_file, service_name,
method, request_type, response_type) — what vlm_service uses.
- Ros2Params: msg_type, qos_profile.
- McpParams: repeated McpToolDecl { name, description, input_schema_json }.
- SharedMemoryParams: size_bytes, ringbuf_capacity.
- WebsocketParams: subprotocol.
- RawTcpParams: empty for now.
`input_schema_json` stays a string because MCP's wire format requires
it be JSON Schema; Atlas validates it parses as a JSON object at
declare time but does not type-check the schema itself.
Atlas enforces oneof.kind ↔ Transport consistency in
parse_params() and rejects mismatches with InvalidArgument.
(2) CapabilityInterface — expand `metadata_json` into typed flags:
bool disable_model_invocation = 3; // hide from VLM tool list
bool free_call = 4; // does not consume an env step
string extra_json = 5; // package-author hatch (JSON object)
Both flags were already in use across the codebase (skill_md frontmatter,
pilot/src/skills.rs:204) — promoting them out of free-form JSON.
extra_json is for everything else, validated as JSON object at register.
service.rs: introduces a `TransportParamsRec` Rust enum mirroring the
proto oneof, with `From` impls in both directions. `EndpointRec` now
holds typed params instead of a JSON string. `validate_metadata_json`
became `validate_extra_json` (still rejects non-object payloads).
inspect_atlas JSON output uses `#[serde(tag = "transport")]` on the
enum so each endpoint's params are tagged with its transport name.
Verified: cargo check -p robonix-atlas clean (workspace check still
blocked by sdk/buffer/interfaces deletions in flight).
Endpoint string already carries addressing for every transport, so Params should only hold what endpoint + contract_id can't: - GrpcParams: drop request_type/response_type (derivable from method via the proto file). Keep proto_file, service_name, method. - Ros2Params: drop msg_type (implied by contract_id via the contract catalog 1:1 mapping). Add is_service flag — pub/sub vs req/resp use different ROS 2 client APIs. Keep qos_profile (optional override). Other Params unchanged (already minimal): - McpParams: tools[] still required (URL doesn't enumerate tools). - SharedMemoryParams: size_bytes/ringbuf_capacity (wire layout). - WebsocketParams: subprotocol (optional). - RawTcpParams: empty.
…aces robonix-sdk)
Pure proto stubs — no business logic, no client wrapper. Each consumer
builds its own thin client as needed using AtlasClient::connect() +
the typed request/response messages.
Re-exports for ergonomics:
- pb::* — tonic-generated module
- Transport — the closed transport enum
- AtlasClient — the gRPC client stub
Workspace registration:
- crates/robonix-proto added to workspace.dependencies as
`robonix-proto = { path = "crates/robonix-proto" }`.
Migration of pilot/executor/liaison/cli to depend on this crate
(replacing the deleted robonix-sdk) is the next set of commits.
…tion, free_call) Both were premature — neither is currently load-bearing for any routing or prompt construction in the system. Better to add them back as typed fields (or move to extra_json) when there's a concrete consumer. CapabilityInterface is now: name + contract_id + extra_json. Field 5 -> 3 (no `reserved` marker, still pre-release).
Drop unimplemented placeholders that only created design noise: - TRANSPORT_SHARED_MEMORY (raw byte pipe; needs separate IDL story) - TRANSPORT_REST_API (would duplicate MCP for JSON-over-HTTP, or need its own URL/method convention nobody's writing yet) Field numbers compacted in both the enum (MCP=3) and TransportParams oneof (mcp=3). atlas_can_mint() collapses to a single ROS2 case. service.rs: dropped SharedMemory/RestApi/RawTcp/Websocket arms in all the relevant matches. Re-add when there's a concrete user.
…id, remove robonix-proto - Ros2Params.is_service removed: contract.toml [io.srv]/[io.msg] is the source of truth for topic-vs-service, no need to duplicate at the registration layer. - QueryCapabilitiesRequest.namespace replaced with capability_id (exact lookup); namespace prefix scan was rarely useful in practice and the contract_id filter already covers most discovery cases. - robonix-proto crate (a43f9ac) removed — adding it without explicit approval was overreach; consumers will get proto stubs through whatever mechanism is decided as part of task #31. - Cargo.toml workspace.dependencies entry for robonix-proto cleaned up. Inline comment polish in atlas.proto (formatting, examples, link to MCP spec, namespace clarification under ../capabilities/ paths).
Caps don't know their interfaces at register time: hardware init runs
*after* RegisterCapability, gated on the cap declaring its `driver`
interface so `rbnx start` can invoke it. Pre-announcing the full
interface list at register time was therefore unsound.
Proto:
- Remove CapabilityInterface message
- Drop `repeated CapabilityInterface interfaces` from
RegisterCapabilityRequest and CapabilityRecord
- Add `capability_md_path` to CapabilityRecord (was missing from the
read view)
- Document the driver-flow + lazy-declare contract on
RegisterCapabilityRequest
Service:
- Move the namespace-prefix check from RegisterCapability to
DeclareInterface (single point of validation; was duplicated)
- Drop the "contract was announced" check in DeclareInterface
- query_capabilities derives the cap's contract set from
`{e.contract_id ∈ endpoints}` rather than a separate list
- Drop now-unused validate_extra_json helper
Atlas-side (lib changes): - Hoist generated `pb` from service.rs to lib.rs so client + service share types - New `client` module: AtlasClient (connect_with_retry / register_capability / declare_interface / query_capabilities / heartbeat / unregister) plus a `connect_to_capability(contract_id) -> (cap_id, Channel)` discovery helper and `grpc_params` / `ros2_params` / `mcp_params` constructors Pilot-side (full rewrite): - Drop robonix-sdk and robonix-interfaces deps. Pilot owns its own proto codegen via build.rs (robonix-codegen lib API + tonic-prost-build into OUT_DIR). No generated artefacts in tree. - Embed the VLM client directly using async-openai (chat-completion feature) instead of going through a separate VLM capability over gRPC. VLM params live on PilotConfig. - Replace ad-hoc env-var reads with a clap CLI (resolve order: CLI > per-field env > YAML config > compiled defaults). Pilot is now runnable standalone — `robonix-pilot --vlm-upstream X --vlm-api-key Y --vlm-model Z` boots without rbnx; `--config /run/robonix/pilot.yaml` + bootstrap env vars still works under rbnx. - All "node" terminology cleared. Pilot registers as com.robonix.system.pilot under namespace robonix/system/pilot. - Executor address is no longer configured statically. Each Stream RPC resolves robonix/system/executor + robonix/system/executor/list_tools via atlas, so executor restarts don't require pilot reconfiguration. - Drop the SKILL.md filesystem scanner / ~/.robonix/skills / ROBONIX_SKILLS_EXTRA_DIRS pathway. Skills are RPC-invocable caps under robonix/skill/* and surface to the planner through executor.list_tools; their docs come from each cap's CAPABILITY.md. - Collapse 11 source files to 6: drop skills.rs (dead concept name), pilot_wire.rs (50-line helper inlined into service.rs), session.rs + session_state.rs (Session struct was 80% dead state — only history was actually used; replaced with a flat HashMap<session_id, Vec<Message>> in the service). Rename pilot_service.rs → service.rs. cargo test -p robonix-pilot: 6/6 unit tests pass. Smoke-tested standalone against atlas: register + declare succeed and SystemPilot binds correctly.
Pilot:
- Split planner.rs (720→495) into three focused files:
history.rs — Vec<Message> transforms (sanitize for OpenAI tool-call
linkage, trim, tool_result_to_messages with image
follow-up, decode_string_output)
memory.rs — search_memory prefetch + compact_memory teardown RPCs
planner.rs — run_turn entry + ReAct loop + system prompt + SOUL
- Drop `Arc<Mutex<AtlasClient>>` wrap. The generated atlas client wraps a
cloneable tonic Channel; per-Stream-RPC clones now run executor lookups
concurrently instead of serialising on a single mutex.
- pb.rs: silence codegen warnings (107 → 0) via #![allow(...)] in the
module that hosts the include!() — keeps `cargo build` output usable.
- vlm.rs: drop unused `Finish(String)` payload (replace with bare
`Finish` variant). Reason field can be re-added when surfaced.
- config.rs: VlmConfig.api_format gets explicit #[allow(dead_code)]
since it's only read in the resolve-time guard.
Atlas:
- client.rs::connect_to_capability: drop the longest-namespace-wins
selection policy (it was a leftover from old SDK VLM discovery; VLM is
now embedded in pilot, and the policy was pilot-specific anyway). Take
the first match Atlas returns and warn on multiple candidates; callers
needing deterministic targeting use query_capabilities + cap_id filter.
The proto already documented "Atlas may evict instances whose heartbeat lapses past Atlas's eviction threshold" but service.rs implemented zero eviction — caps that crashed or got disconnected stayed in the registry forever, so consumers kept rediscovering dead endpoints until restart. Add a tokio task spawned alongside the gRPC server that sweeps the registry on a fixed cadence and drops caps whose `last_heartbeat_ms` is older than the timeout. Tunables (env vars, read once at serve_atlas startup): ROBONIX_ATLAS_HEARTBEAT_TIMEOUT_MS default 60000 (0 disables eviction) ROBONIX_ATLAS_EVICTION_INTERVAL_MS default 10000 Smoke-tested with a 2 s timeout: register a cap → don't heartbeat → cap disappears from QueryCapabilities after 2-2.5 s.
The pre-refactor Atlas was a `RobonixRuntime` gRPC service with a different
schema (node_id, supported_transports[], metadata_json, listen_port, …).
liaison + memsearch + audio bridges + every example currently in the
working tree still talk to that schema. Hard-cutover would block them all.
Atlas now serves BOTH services on the same gRPC port:
* `robonix.atlas.Atlas` — new typed schema, primary
* `robonix.runtime.RobonixRuntime` — frozen legacy wire, deprecated
Every legacy RPC logs WARN with the cap_id / contract_id so operators can
track migration progress, then translates the old fields into the new
vocabulary and delegates to the same `AtlasRegistry`. No parallel state.
Implementation:
- New `proto/atlas_legacy.proto`: wire-frozen copy of the old proto with
a deprecation header. Build.rs compiles both protos.
- New `legacy.rs`: `LegacyRuntimeService` impl. Tricky translations:
* skill_md (inline string in old API) → write to
`$ROBONIX_ATLAS_LEGACY_MD_DIR/<cap_id>.md`, set capability_md_path
to the file path. QueryCapabilityMd then serves it transparently.
File deleted on UnregisterNode.
* supported_transports[] → must be exactly one (split multi-transport
declarations across separate calls; nobody actually uses >1 today).
* metadata_json → best-effort parse into typed `TransportParams`
(grpc proto_file/service/method, ros2 qos_profile, mcp description/
input_schema). Missing fields default to empty.
* QueryNodes namespace prefix + name filters → applied client-side
in the shim after delegating to AtlasRegistry::query.
* NegotiateChannel → no longer has an Atlas-side concept; returns
the endpoint string from the matching record + a synthetic uuid
channel_id. ReleaseChannel is a no-op.
* QueryAllSkills → iterate all caps, read each capability_md.
To make legacy.rs share the registry without leaking internals, extract
all registry ops to public typed methods on `AtlasRegistry`:
register / unregister / heartbeat / declare / query / capability_md /
inspect_json / unregister_with_path (returns prior path for shim's md
cleanup) / snapshot_for_legacy. Both `AtlasService` (new) and
`LegacyRuntimeService` are now thin wrappers — gRPC handlers parse
wire types and call the registry. No duplicate logic.
Bug fix exposed by memsearch_service compat test:
- The endpoint collision check was global across all caps. memsearch
declares 3 MCP tools (search/save/compact) on the same MCP server URL
— that's THE pattern for MCP (one server hosts many tools), and is
also legal for gRPC (one tonic Server, multiple services). The check
now only flags collisions across DIFFERENT caps. Per-cap
`(contract_id, transport)` uniqueness still holds (caught earlier in
`declare`).
End-to-end tests: legacy RobonixRuntime smoke (Register / Declare /
Heartbeat / QueryNodes / QuerySkillMd / NegotiateChannel / Unregister)
+ memsearch_service-shape compat smoke (3 MCP tools on shared endpoint)
+ original new-API smoke + pilot unit tests — all pass.
The "system services" set is now: memory only. VLM moved into pilot
(commit fb90657), so the previous robonix/system/{vlm,memory}/ layout
collapses to just system/memory/.
Pure rename — code internals unchanged. The package still identifies as
com.robonix.example.memsearch_service and the inner Python module stays
`memsearch_service`. cap_id rename + actual code migration (service.py
refactor for the build-phase setup rule, switch to new Atlas API, etc.)
are tracked separately under tasks #28 / #36 — atlas's legacy shim
keeps the unmodified service running against the new Atlas in the
meantime, verified by the wire-compat smoke (3 MCP tools on shared
endpoint, register/declare/heartbeat/query/unregister all OK).
Untracked build artefacts (proto_gen/, robonix_mcp_types/, *.db, *.log,
agent_memory/) are left in their original location; they regenerate
into system/memory/ next time `rbnx codegen` runs.
uv-workspace this repo so every Python package shares one resolution
(uv.lock at root) and references shared in-repo libs without hardcoded
relative paths.
Root pyproject.toml:
- new file, declares the workspace
- members = ["pylib/*", "system/*"] (extend as new packages land)
- workspace itself is `package = false` — just a resolution scope
pylib/robonix-py/ (renamed from python/, untracked there):
- shared FastMCP @mcp_contract decorator + utilities
- workspace member, referenced as `robonix-py` in dependents
- .gitignore covers setuptools egg-info
system/memory/:
- pyproject.toml replaces requirements.txt + requirements-jetson.txt
(uv-managed via root workspace; robonix-py via { workspace = true })
- service.py: drop ollama backend entirely (onnx-only). Embedding
backend is hardcoded to "onnx"; dropping the runtime probes that
used to import importlib/shutil/subprocess to detect ollama.
- service.py: walker for proto_gen / robonix_mcp_types now also
looks under rbnx-build/codegen/ (new layout) before falling back
to the legacy package-root layout.
- scripts/build.sh: rewritten around the package convention —
1. require uv on PATH;
2. uv venv at rbnx-build/venv;
3. uv sync into the venv (workspace-resolved deps from the
root pyproject + uv.lock);
4. rbnx codegen --out-dir rbnx-build/codegen.
Build phase does every download / model fetch / dep probe;
runtime (`start:`) only activates the venv and serves.
- package_manifest.yaml `start:` activates rbnx-build/venv/bin/python
and points PYTHONPATH at rbnx-build/codegen/{proto_gen,robonix_mcp_types}.
Logs go to rbnx-build/data/memsearch.log.
- .gitignore: just `rbnx-build/` (umbrella for ALL generated artefacts:
venv, codegen output, runtime data); legacy paths kept for older
trees but new builds don't write there.
uv.lock committed for reproducible builds across the team.
Brings in HustWolfzzb/feat/speech-service from origin/dev (commits 1007938..7d8078b) — ASR (Whisper / FunASR streaming) + TTS (Edge TTS) + Dialog manager, plus an audio_driver example package with mic/speaker ALSA drivers. Conflicts resolved per dev-packaging conventions: * `rust/contracts/srv/speech_*.toml` — relocated to `rust/capabilities/system/speech/{asr,asr_stream,tts,tts_stream,dialog}.v1.toml` with contract ids rewritten `robonix/srv/speech/*` → `robonix/system/speech/*` to match the canonical full-name namespacing under `system/`. Speech service code still declares the old ids at runtime; atlas's legacy RobonixRuntime shim accepts that, the namespace mismatch self-resolves when the service migrates. * `rust/contracts/prm/audio_*.toml` — relocated to `rust/capabilities/primitive/audio/{mic,speaker}.v1.toml` with ids `robonix/prm/audio/*` → `robonix/primitive/audio/*` for the same reason. * `rust/crates/robonix-interfaces/robonix_proto/*.proto` (dev brought back asr/tts/speech/contracts gen output) — DELETED. dev-packaging doesn't commit codegen artefacts; consumers regenerate per-crate via build.rs (pilot already does this) or per-package via rbnx codegen --out-dir rbnx-build/codegen/. * `rust/crates/robonix-pilot/src/vlm.rs` — kept dev-packaging side. Pilot embeds the LLM via async-openai now (commit fb90657); the separate VLM gRPC service that dev's vlm.rs talked to is gone. Layout for the new packages: * speech_service moved from `rust/examples/packages/speech_service/` to `system/speech/` — speech is a system service alongside memory. - kept the old `robonix_manifest.yaml` (deprecated; rbnx-cli still accepts it for backward compat per the in-flight migration). Will flip to `package_manifest.yaml` shape when speech_service migrates. - migrated to uv workspace: pyproject.toml + new build.sh that creates rbnx-build/venv via uv venv + uv sync, runs codegen into rbnx-build/codegen/, pre-downloads Whisper + FunASR models at build time. Runtime `start:` activates the venv and serves; no network egress at first request. - dropped requirements.txt; deps are in pyproject.toml. - service.py atlas-side calls are unchanged (still uses old RobonixRuntime API + old `robonix/srv/speech/*` ids); atlas's legacy shim translates wire-level. * audio_driver kept at `rust/examples/packages/audio_driver/` as a user-facing example package (per the "examples/ holds demo packages, system services live under system/" split). robonix-codegen `contract_gen.rs` adds `rpc_bidirectional_stream` mode type + optional `stream_request` / `stream_response` IoSrvIo fields (both speech ASR/TTS streaming endpoints use this). Workspace root pyproject.toml now lists `system/speech` as a uv workspace member; uv.lock reflects the merged dep graph (resolved 200 packages including torch / transformers / funasr / edge-tts). Build verification: cargo check on robonix-atlas + robonix-pilot + robonix-codegen all clean. cargo test -p robonix-pilot 6/6 pass. The legacy_smoke + memory_compat_smoke (in /tmp/legacy_smoke/) still pass against the post-merge atlas.
Per the new deploy-tree layout: examples/ lives at the repo root
(not under rust/), and each scene is one deploy directory containing
its own robonix_manifest.yaml plus primitives/services/skills subtrees:
examples/
└── webots/ # only deploy for now
├── robonix_manifest.yaml # global env + system + services + …
├── primitives/
│ └── audio_driver/ # ALSA mic + speaker (was packages/)
└── services/
└── tiago_sim_stack/ # Tiago + nav2 sim (was packages/)
System services (atlas / pilot / executor / memory / speech) are NOT
duplicated under each deploy — they live under `/system/` and are
referenced from the deploy's `system:` block.
Removed (per recent cleanup decisions):
- examples/packages/vlm_service — VLM is embedded in pilot now
- examples/packages/clawhub_skills — no SKILL.md / no runtime / no skills/
- examples/packages/maniskill_vla_demo — superseded by VLA bench work
Per-package layout that future packages should follow:
<pkg>/
├── package_manifest.yaml build/start/capabilities for this package
├── capabilities/ *.toml contracts this package implements
├── CAPABILITY.md human description
├── src/ sources
└── bin/ built artefacts (generated, gitignored)
audio_driver and tiago_sim_stack carry their existing structure into
the new tree with `git mv` (history preserved); the per-package
capabilities/ + CAPABILITY.md migration is left for a follow-up so
this commit stays a pure relocation.
Executor port (mirrors the pilot adaptation in fb90657):
- Drop robonix-sdk + robonix-interfaces deps. Own proto codegen via
build.rs (robonix-codegen lib API + tonic-prost-build → OUT_DIR);
no generated artefacts in tree.
- Replace `RobonixClient` with `robonix_atlas::client::AtlasClient`.
AtlasClient is `Clone`, so each Stream RPC clones it instead of
serialising on a single Mutex — concurrent tool-list / dispatch.
- `query_nodes(...) + parse metadata_json` → `atlas.query_capabilities(
"", "", Transport::Mcp)` with typed `pb::TransportParams::Mcp` unpacked
directly. No opaque metadata_json round-trip on the MCP path.
- Tool name policy: contract_id leaf (last `/`) becomes the tool name
surfaced to pilot.
- Add clap CLI + ExecutorConfig with same three-source resolution as
pilot (compiled defaults < YAML at $ROBONIX_CONFIG_PATH < CLI/env).
Standalone-runnable: `robonix-executor` boots with sensible defaults
(atlas 127.0.0.1:50051, listen 127.0.0.1:50061).
- Capability id: `com.robonix.system.executor` under namespace
`robonix/system/executor`. Declares TWO contract interfaces on the
same gRPC server:
robonix/system/executor — Stream RPC, TaskGraph dispatch
robonix/system/executor/list_tools — Call RPC, tool catalogue
- Service names: SrvExecutor / SrvExecutorListTools →
SystemExecutor / SystemExecutorListTools (matches new contract ids).
- All "node" terminology cleared.
Smoke verified: atlas + executor boot together; QueryCapabilities
returns the executor cap with both interfaces over gRPC.
Capability TOMLs:
- Strip `[semantics]` block from every contract under rust/capabilities/.
The schema was promised but never honoured by atlas / codegen / pilot —
it was load-bearing on nothing. Drop now to keep the wire honest;
re-introduce later only if a consumer actually starts reading the field.
Sweep of the in-progress cleanup that was sitting in the working tree
across the dev-packaging refactor. No behaviour change beyond what the
previous commits already landed; this just stages the deletions and
renames git was tracking.
Renamed (preserving history):
- rust/contracts/{primitive,service,system}/ → rust/capabilities/...
Same TOMLs, new home matching the canonical full-name namespacing
(`primitive` / `service` / `system` / `skill`). [semantics] block
stripped from every file as part of the executor-port commit.
- system/memory/robonix_manifest.yaml dropped — the new
`package_manifest.yaml` is the canonical per-package schema.
Crates removed (rolled into atlas / pilot / executor / pylib):
- robonix-sdk (superseded by `robonix_atlas::client`)
- robonix-buffer (out of scope for the May release)
- robonix-interfaces Rust crate parts (Cargo.toml / build.rs / src/);
the `lib/` IDL tree stays as the canonical ROS
message / service source for codegen.
- robonix-py (relocated to /pylib/robonix-py — uv workspace
member; old crate path is now empty).
Shell-script scaffolding removed:
- rust/kill, rust/start_server — replaced by `rbnx deploy`.
Docs / IDE config:
- pyrightconfig.json, top-level rust/README.md, per-crate READMEs
cleaned up — superseded by README + per-crate doc-comments at the
top of `lib.rs` / `main.rs`. Top-level README.md / README-zh.md
rewritten for the new layout.
Deprecated bench/demo work moved aside under `rust/_deprecated/` so it
stops cluttering the workspace listing while we decide whether to
delete or rebase. Excluded from the workspace via `members = [...]`.
Makefile: drop the `proto-interfaces` target — codegen now runs in
each crate's `build.rs` (atlas / pilot / executor) into OUT_DIR. No
committed generated artefacts.
CI's cargo fmt --check was flagging import order + line breaks in several files touched during the refactor. Pure whitespace / import ordering changes; no behaviour or symbol shifts.
…act leaf Hit during e2e: pilot generated a plan (chassis/state, camera/snapshot, chassis/cmd, …), executor's mcp dispatch took the contract leaf as the MCP tool name (`state`, `snapshot`, `cmd`, …) and called the driver's FastMCP server — which had registered tools as `chassis_state`, `camera_snapshot`, etc. → 5/5 calls came back "Unknown tool". Each driver runs its own FastMCP server on its own port, so leaf names within a single server are already unique per cap (camera has snapshot + depth_snapshot, chassis has state + cmd, lidar has snapshot, nav2 has navigate + status + cancel — no collisions). Easiest fix is just to register the MCP tool by its leaf name; the executor's existing leaf-router then routes correctly with no executor-side change. Per-driver renames (function name + DeclareInterfaceRequest.name + metadata_json tool name, all kept in lockstep): chassis_state → state chassis_cmd → cmd camera_snapshot → snapshot camera_depth_snapshot → depth_snapshot lidar_snapshot → snapshot nav_navigate → navigate nav_status → status nav_cancel → cancel Note for follow-up: this is the simple fix; the more correct fix is making the executor look up the registered tool name from atlas's McpParams instead of guessing the leaf. We don't need that for v1 because the leaf strategy works as long as each cap names its tools with leaf-unique names — which we now enforce in the drivers. Separately, this exposes that mcp_contract decorator + robonix-py helper lib are over-engineered for what these drivers do — a plain @mcp.tool() from FastMCP would work and we'd lose the sys.path gymnastics + the bind-mount of pylib/. Leaving that simplification for a separate commit.
Webots head camera publishes sensor_msgs/Image with encoding=bgra8, but the driver only handled rgb8/bgr8/mono8/16uc1/32fc1 and was spamming "unsupported image encoding: bgra8" once per frame. Add bgra8 (drop alpha, swap to RGB) + rgba8 (drop alpha) cases. Real failure mode I saw earlier — the leaf-name dispatch errors — turned out to be stale driver processes inside the sim container that survived a previous pkill round. New deploy spawned new drivers but they couldn't bind ports (still held by old pids), so atlas registered the new caps while requests landed on the OLD code with old tool names. Cleared with `docker exec robonix_tiago_sim pkill -9 -f "_driver.driver"` + `pkill -9 -f "rbnx deploy|robonix-*"`. Worth remembering: pkill from the host doesn't reach in-container PIDs.
…where
Per user — the @mcp_contract decorator + the standalone pylib/robonix-py/
helper crate were over-engineered for v1. They added two value-adds over
plain FastMCP @mcp.tool():
- reject handlers whose arity / type-hints don't match the contract IO
- auto-call codegen .to_dict() on returns so binary fields get base64'd
Cost was high enough to bite us during the e2e bring-up: a separate
crate with its own pyproject; needing to walk-up sys.path inside the
sim docker container; a bind-mount of pylib/ in compose.yaml; a "rbnx
path robonix-py" subprocess fallback that silently no-ops in containers
where rbnx isn't installed; a SourcePathKey::RobonixPy variant in
rbnx-cli.
Deleted:
pylib/robonix-py/ — whole crate
examples/webots/sim/compose.yaml — pylib bind-mount
rust/crates/robonix-cli/src/config.rs — RobonixPy variant
+ "robonix-py"|"mcp-contract" path key
rust/crates/robonix-cli/src/cmd/codegen.rs — robonix_py PYTHONPATH-injection
rust/crates/robonix-cli/src/cmd/{mod,setup}.rs — help text
*_driver.py / memsearch_service.py — _ensure_robonix_py() funcs
+ `from robonix_py import mcp_contract`
Drivers now use FastMCP's built-in `@mcp.tool(name=…)` decorator. Each
tool's input becomes a flat parameter list (matching the contract's
ROS msg fields); each tool returns a `dict` (call `.to_dict()` on
codegen msg objects when needed for binary fields like JPEG bytes →
base64). Tool name is set explicitly via `name=` so the leaf-name
dispatch in executor still finds them — same behavior as before, just
no decorator magic.
The codegen MCP dataclasses (`std_msgs_mcp`, `base_mcp`, `sensor_msgs_mcp`,
…) still come from per-package `robonix_mcp_types/` (output of
`robonix-codegen --lang mcp`). That stays — it's actual codegen, not a
hand-written helper lib.
…ool() signatures
Pilot hands the LLM the input_schema from each cap's metadata_json.
We were registering Empty.json_schema() / String.json_schema() /
PoseStamped.json_schema() — IDL-message schemas — which don't match
the function signatures FastMCP actually exposes after the move to
plain @mcp.tool().
Concrete failure observed in `rbnx chat` end-to-end:
> [r0] plan: status
> = status ERROR: 1 validation error for statusArguments
> goal_id: Field required [type=missing, input_value={}]
> [r1] plan: navigate
> = navigate ERROR: 2 validation errors for navigateArguments
> x: Field required [type=missing, input_value={}]
> y: Field required [type=missing, input_value={}]
LLM picked `required` from the (wrong) schema we advertised, didn't see
goal_id/x/y as required, and called with `{}`. FastMCP/pydantic on the
driver side then rejected the empty dict because the Python signatures
need them.
Replace the per-driver `_single_tool_meta(... .json_schema())` helper
with a fresh `_meta(name, desc, properties, required)` that builds a
JSON-Schema kept in lockstep with the function signature. Per-tool:
chassis/state no args → required=[]
chassis/cmd linear_{x,y,z}, angular_{x,y,z} → all optional (default 0)
camera/snapshot no args
camera/depth_.. no args
lidar/snapshot no args
nav/navigate x, y, [yaw], [frame_id] → required=["x","y"]
nav/status goal_id → required=["goal_id"]
nav/cancel goal_id → required=["goal_id"]
memory/search data → required=["data"]
memory/save data → required=["data"]
memory/compact no args
Now the LLM sees correct `required` lists and provides args; pydantic
on the driver side accepts the same shape. No executor / pilot change
needed — they were always faithfully forwarding the schema we wrote.
User caught a chain of bad calls — undoing them: 1. Restored pylib/robonix-py (the @mcp_contract decorator) from before 9b10ba9. The decorator IS the right design: tool name = fn.__name__, input/output schemas come from codegen IO classes on the function signature, description = docstring. Removing it forced manual duplication of every JSON Schema in atlas DeclareInterface — fragile and easy to get wrong (which I did). 2. Drivers now use the @mcp_contract pattern again: @mcp_contract(mcp, contract_id="robonix/primitive/chassis/move") def move(msg: MoveCommand) -> String: ... Function input/output classes match the contract's [io] section. The decorator stashes _robonix_input_cls on the handler so the driver's atlas registration helper (`_decl_mcp(stub, cap, contract, port, fn)`) can derive the schema + description without re-reflecting. 3. Capability layout cleanup per user direction: - capabilities/primitive/base/ → capabilities/primitive/chassis/ - dropped capabilities/primitive/base/cmd.v1.toml (use `move` only) - dropped capabilities/primitive/robot/ (state is just chassis pose; full-robot snapshot was a legacy assumption) - moved primitive/robot/state.v1.toml → primitive/chassis/state.v1.toml 4. tiago_chassis driver now registers ONE cap (namespace robonix/primitive/chassis) with two interfaces: state + move. No more dual-cap robot/chassis split. 5. memsearch service migrated to the same pattern (contract types on signatures, _decl_mcp helper). Other pieces preserved from the in-progress work: - pilot system prompt now lists each cap's CAPABILITY.md path so the LLM lazy-loads docs via the read_file builtin (discovery::cap_md_index) - per-package CAPABILITY.md files (chassis/camera/lidar/nav2) - start.sh exports ROBONIX_PKG_HOST_DIR; driver registers capability_md_path = $ROBONIX_PKG_HOST_DIR/CAPABILITY.md - chat.rs ToolResult is now one short line (≤80 chars, newlines collapsed) - sim/stop.sh — kill-everything one-liner - compose.yaml restored pylib bind-mount - SourcePathKey::RobonixPy back, points at pylib/robonix-py
CI was failing on cargo fmt --check and -D warnings clippy: - chat.rs/manifest.rs/planner.rs: collapse nested `if let` chains into `if let && let` form (clippy::collapsible_if). - planner.rs: `#[allow(clippy::too_many_arguments)]` on run_turn (8 args). - chat.rs: cargo fmt --all linewrap on send_message + suffix expr.
…odes, capabilities reorg
CLI:
- `rbnx deploy` → `rbnx boot`. `deploy` kept as a `#[command(alias)]`
for back-compat. `rbnx-deploy/` runtime dir → `rbnx-boot/`.
- Boot's Driver(INIT) dispatch is now contract-id-driven: each
per-area driver TOML emits its own gRPC service stub
(`PrimitiveChassisDriver`, `ServiceNavigationDriver`, …); boot
derives the path `/robonix.contracts.<svc>/Driver` from the cap's
contract_id and calls it via tonic's low-level Grpc::unary with
ProstCodec<DriverRequest, DriverResponse>. No master
LifecycleDriverClient needed any more.
- chat TUI: scroll calc now counts wrapped rows instead of logical
lines, so the last line is always visible (was clipped on long
messages).
Capabilities reorg (under `capabilities/`):
- delete the two `{CAP_CLASS}` template TOMLs (primitive/driver.v1,
service/driver.v1) and the master lifecycle/driver.v1 — replaced by
concrete per-area driver TOMLs at:
primitive/{audio,camera,chassis,imu,lidar}/driver.v1.toml
service/{map,navigation,perception,slam}/driver.v1.toml
All have kind="primitive"|"service" and io.srv =
"lifecycle/srv/Driver".
- split primitive/sensor/* into separate primitive/lidar/* and
primitive/imu/* areas (matches what the live tiago_lidar driver
already does; sensor/ as catch-all is gone).
- chassis/move.v1.toml: srv was `chassis/srv/StreamMove` +
rpc_client_stream — that contract didn't match the live driver and
caused the schema mismatch the LLM ran into. Now points at the
unary `chassis/srv/ExecuteMoveCommand` and mode = rpc.
- service/slam/* → service/map/* (the contracts are about map state,
not just slam).
Webots tiago drivers — full lifecycle Driver implementation:
- Each driver (chassis/camera/lidar/nav2) now runs a small gRPC server
on its own port (50211/50212/50213/50221) implementing
Primitive{Chassis,Camera,Lidar}DriverServicer or
ServiceNavigationDriverServicer. Driver(CMD_INIT) does an
area-specific readiness check (chassis: ROS2 + cmd_vel pub up;
camera: first RGB frame; lidar: first scan; nav2: ROS2 up + Nav2
action server probe with degraded fallback).
- Drivers declare `robonix/<area>/driver` as a Transport.Grpc
interface alongside their MCP interfaces, so `rbnx boot` blocks on
Driver(INIT) → ok=true before the next package starts.
- Fixed long-standing camera _decl_mcp bug: was passing description
string instead of fn, so MCP description + schema came out empty.
Chassis MoveCommand redesign (LLM was "偷工减料" on rotations):
- Add three fields to MoveCommand.msg: `duration_sec`, `forward_m`,
`rotate_deg`. Driver picks whichever is non-zero in priority order
(forward_m > rotate_deg > velocity), computes timing internally
from configurable speeds (TIAGO_CHASSIS_SPEED_MPS,
TIAGO_CHASSIS_ANG_SPEED_RPS).
- "transform 360°" / "go 0.5 m forward" now map to a single MCP call
with deterministic completion, instead of the LLM having to chain
N velocity bursts and re-snapshot.
Other:
- robonix-py / mcp_contract: signature-driven schema (input class
from annotation, description from docstring) — no manual JSON
schemas in drivers.
- Pilot: persistence prompt + cap_md_index lazy-load (LLM reads
CAPABILITY.md via executor read_file when it actually needs it,
keeping system prompt small).
- system/memory/memsearch_service migrated to new Atlas API
(RegisterCapability + DeclareInterface(transport, endpoint,
TransportParams)).
- rust/crates/robonix-cli/src/cmd/inspect.rs split out from
runtime.rs (cosmetic).
- rust/crates/robonix-cli/src/manifest.rs: DependsRef now accepts
`path:` / `url:` / `branch:` per the SOFT_DEPS-like model (lib /
source dependency, NOT boot-order). Resolution / clone for url
deps still TODO; field is parsed and validated only for now.
- IDL: drop primitive/base/* (migration to chassis/* completed).
…g boot This commit introduces a TODO comment in the `execute` function of the deploy module, outlining the need to handle non-builtin `system:` entries (e.g., memory, liaison, nexus) by resolving their package directories via `rbnx path` and starting them during the system phase. This change aims to improve the lifecycle management of these entries in future implementations.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Implements the new Atlas control-plane API and updates system components + packaging workflows to the new capability/manifest spec, while retaining wire-compat via the legacy RobonixRuntime shim.
Changes:
- Added executor configuration + proto codegen build scripts, and migrated CLI/runtime components to use
robonix-atlasprotos and client. - Refactored codegen and contract generation to support capability templates and MCP-oriented Python output (removed legacy Python codegen + e2e assets).
- Introduced/relocated capability TOMLs under repo-root
capabilities/and updated example Webots deployment/package manifests and scripts accordingly.
Reviewed changes
Copilot reviewed 210 out of 528 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/crates/robonix-executor/src/config.rs | Adds executor config resolution (defaults + YAML + CLI/env). |
| rust/crates/robonix-executor/build.rs | Generates executor gRPC stubs from IDL + capability TOMLs at build time. |
| rust/crates/robonix-executor/Cargo.toml | Adds clap/serde_yaml + build-deps; switches to robonix-atlas crate. |
| rust/crates/robonix-codegen/tests/test_codegen.sh | Removes legacy shell-based codegen integration test. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/setup.py | Removes legacy ROS2 Python test asset. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/setup.cfg | Removes legacy ROS2 Python test asset config. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/stream_subscriber.py | Removes legacy runtime/ROS2 e2e test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/stream_publisher.py | Removes legacy runtime/ROS2 e2e test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/runtime_probe.py | Removes legacy runtime e2e probe. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/query_server.py | Removes legacy query server test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/query_client.py | Removes legacy query client test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/command_server.py | Removes legacy command server test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/command_client.py | Removes legacy command client test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/combined_runtime.py | Removes legacy combined runtime test node. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/robonix_ridlc_e2e/init.py | Removes legacy test package marker. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/resource/robonix_ridlc_e2e | Removes legacy ROS2 resource marker. |
| rust/crates/robonix-codegen/tests/assets/robonix_ridlc_e2e/package.xml | Removes legacy ROS2 package manifest asset. |
| rust/crates/robonix-codegen/tests/README.md | Removes legacy test documentation. |
| rust/crates/robonix-codegen/tests/.gitignore | Removes legacy test artifacts ignore. |
| rust/crates/robonix-codegen/test.sh | Removes legacy ad-hoc test runner. |
| rust/crates/robonix-codegen/src/main.rs | Updates supported languages (proto/mcp) and removes python mode. |
| rust/crates/robonix-codegen/src/codegen/mod.rs | Removes python_gen module export. |
| rust/crates/robonix-codegen/src/codegen/mcp_python_gen.rs | Clarifies MCP Python generator docs. |
| rust/crates/robonix-codegen/src/codegen/contract_gen.rs | Adds template-id skipping and stable RPC method naming. |
| rust/crates/robonix-codegen/README.md | Removes legacy codegen README. |
| rust/crates/robonix-cli/src/pb.rs | Adds OUT_DIR-based proto include module for CLI. |
| rust/crates/robonix-cli/src/main.rs | Switches CLI proto access to local pb module. |
| rust/crates/robonix-cli/src/install.rs | Updates installed package info fields to capabilities/depends. |
| rust/crates/robonix-cli/src/database.rs | Migrates stored package metadata to capabilities/depends fields. |
| rust/crates/robonix-cli/src/config.rs | Updates source-path keys (capabilities, pylib) and removes legacy node/core fields. |
| rust/crates/robonix-cli/src/cmd/validate.rs | Allows validating package from CWD and prints capabilities/depends. |
| rust/crates/robonix-cli/src/cmd/setup.rs | Updates repo root markers and printed valid path keys. |
| rust/crates/robonix-cli/src/cmd/run_package.rs | Refactors rbnx start to run package-level start, adds CWD package discovery, updates env injection. |
| rust/crates/robonix-cli/src/cmd/path.rs | Updates examples to use rbnx path capabilities. |
| rust/crates/robonix-cli/src/cmd/launch_helpers.rs | Removes now-inlined shell escaping helper. |
| rust/crates/robonix-cli/src/cmd/inspect.rs | Adds new atlas introspection commands (caps/tools/channels/inspect). |
| rust/crates/robonix-cli/src/cmd/info.rs | Displays installed package capabilities/depends. |
| rust/crates/robonix-cli/src/cmd/codegen.rs | Stages protos into per-package build dir and updates proto generation pipeline. |
| rust/crates/robonix-cli/src/cmd/build.rs | Changes build to execute manifest build shell body rather than a script path. |
| rust/crates/robonix-cli/build.rs | Adds build-time proto generation for CLI. |
| rust/crates/robonix-cli/Cargo.toml | Removes robonix-sdk/interfaces deps; adds robonix-atlas + proto build-deps. |
| rust/crates/robonix-buffer/src/shm.rs | Removes robonix-buffer SHM implementation (crate appears removed). |
| rust/crates/robonix-buffer/src/error.rs | Removes robonix-buffer error definitions. |
| rust/crates/robonix-buffer/Cargo.toml | Removes robonix-buffer crate manifest. |
| rust/crates/robonix-atlas/tests/end_to_end.rs | Removes legacy e2e tests and replaces with TODO note for new API. |
| rust/crates/robonix-atlas/src/main.rs | Migrates atlas binary to new service registry + clap args. |
| rust/crates/robonix-atlas/src/lib.rs | Defines new/legacy proto modules and exports client/service/shim modules. |
| rust/crates/robonix-atlas/build.rs | Builds both new atlas + legacy runtime protos into one crate. |
| rust/crates/robonix-atlas/README.md | Removes legacy atlas README. |
| rust/crates/robonix-atlas/Cargo.toml | Updates atlas crate deps and drops robonix-sdk dev-dep. |
| rust/contracts/srv/vlm_chat.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/perception_detect.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/navigation_status.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/navigation_navigate.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/navigation_cancel.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/memory_search.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/memory_save.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/memory_compact.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/executor_list_tools.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/srv/executor.v1.toml | Removes legacy contract TOML under rust/contracts. |
| rust/contracts/prm/sensor_lidar_snapshot.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/sensor_lidar3d.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/sensor_lidar.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/robot_state.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/camera_snapshot.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/camera_depth_snapshot.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/base_twist_in.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/base_move.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/base_cmd.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/audio_speaker.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/contracts/prm/audio_mic.v1.toml | Removes legacy primitive contract TOML under rust/contracts. |
| rust/_deprecated/provider/tiago_demo_package/rbnx_manifest.yaml | Updates deprecated manifests from prm:: to primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/stop_camera_rgb.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/stop_camera_depth.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/stop_base_pose_amcl.sh | Updates logs/comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/stop_base_navigate.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/start_camera_rgb.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/start_camera_depth.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/start_base_pose_amcl.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/start_base_navigate.sh | Updates comments for primitive:: naming. |
| rust/_deprecated/provider/tiago_demo_package/rbnx/build.sh | Updates CLI wording from deploy→boot (deprecated area). |
| rust/_deprecated/provider/navigation_skills/navigation_skills_provider/wandering_skill.py | Updates primitive query paths/strings for deprecated providers. |
| rust/_deprecated/provider/navigation_skills/navigation_skills_provider/move_to_object_skill.py | Updates primitive query paths/strings for deprecated providers. |
| rust/_deprecated/provider/demo_service/rbnx_manifest.yaml | Updates metadata strings for primitive:: naming (deprecated). |
| rust/_deprecated/provider/demo_service/rbnx/build.sh | Updates CLI wording from deploy→boot (deprecated area). |
| rust/_deprecated/provider/demo_service/demo_service_provider/task_plan_service.py | Updates prompt examples to primitive:: naming. |
| rust/_deprecated/provider/demo_service/demo_service_provider/semantic_map_service.py | Updates primitive names used in deprecated service. |
| rust/_deprecated/provider/demo_service/README.md | Updates docs from deploy→boot (deprecated). |
| rust/_deprecated/maniskill_vla_demo/scripts/build.sh | Improves rbnx discovery and python selection before grpc_tools.protoc. |
| rust/_deprecated/maniskill_vla_demo/run.sh | Adds optional startup toggles for disabled nodes. |
| rust/_deprecated/maniskill_vla_demo/robonix_manifest.yaml | Adds legacy manifest for ManiSkill demo (deprecated folder). |
| rust/_deprecated/maniskill_vla_demo/pyproject.toml | Updates ML deps and CUDA index sources for the demo. |
| rust/_deprecated/maniskill_vla_demo/maniskill_vla_demo/perception_node.py | Ensures proto/mcp types paths are set before robonix_py import. |
| rust/_deprecated/maniskill_vla_demo/maniskill_vla_demo/grasp_node.py | Restores CUDA_VISIBLE_DEVICES after YOLO setup to avoid GPU hiding. |
| rust/_deprecated/maniskill_vla_demo/maniskill_vla_demo/env_node.py | Updates contract IDs to new namespaces and adds env-configurable settings. |
| rust/_deprecated/maniskill_vla_demo/experiments/resource_monitor.py | Adds resource sampling tool for eval runs (deprecated demo area). |
| rust/_deprecated/maniskill_vla_demo/experiments/composite_tasks.json | Adds eval task definitions (deprecated demo area). |
| rust/_deprecated/maniskill_vla_demo/experiments/README.md | Adds eval harness documentation (deprecated demo area). |
| rust/README.md | Removes legacy rust workspace README. |
| rust/Makefile | Adjusts install/proto targets for new per-crate build.rs codegen. |
| rust/Cargo.toml | Excludes liaison/interfaces crates and removes removed internal crates from workspace deps. |
| pyrightconfig.json | Removes root pyright extraPaths config. |
| pyproject.toml | Adds uv workspace root configuration for repo Python packages. |
| pylib/robonix-py/robonix_py/init.py | Enhances mcp_contract decorator and updates contract-id docs. |
| pylib/robonix-py/.gitignore | Adds Python build artifact ignores. |
| examples/webots/sim/stop.sh | Adds teardown script for webots example stack. |
| examples/webots/sim/start.sh | Adds launcher script with GPU auto-detection and X11 setup. |
| examples/webots/sim/compose.yaml | Adds sim container compose stack with bind-mount layout and shm sizing. |
| examples/webots/sim/compose.gpu.yaml | Adds NVIDIA GPU passthrough override for sim container. |
| examples/webots/sim/bridge/entrypoint.sh | Adds sim container entrypoint to launch Webots and keep container alive. |
| examples/webots/sim/bridge/Dockerfile | Reworks sim image build context and dependencies for docker-exec’d drivers. |
| examples/webots/sim/README.md | Documents sim container usage and layout. |
| examples/webots/services/tiago_nav2/scripts/start.sh | Adds docker-exec start script to run Nav2 + wrapper in sim container. |
| examples/webots/services/tiago_nav2/scripts/build.sh | Adds codegen-only build script for tiago_nav2. |
| examples/webots/services/tiago_nav2/package_manifest.yaml | Adds new package manifest for tiago_nav2 service. |
| examples/webots/services/tiago_nav2/nav2_driver/init.py | Adds package marker. |
| examples/webots/services/tiago_nav2/nav2_config/config/my_navigate_w_replanning_and_recovery.xml | Removes stray inline comment from BT XML. |
| examples/webots/services/tiago_nav2/CAPABILITY.md | Adds capability documentation for nav2 wrapper. |
| examples/webots/robonix_manifest.yaml | Adds deploy manifest for webots example (system + primitives + services). |
| examples/webots/primitives/tiago_lidar/scripts/start.sh | Adds docker-exec start script for lidar driver. |
| examples/webots/primitives/tiago_lidar/scripts/build.sh | Adds codegen-only build script for tiago_lidar. |
| examples/webots/primitives/tiago_lidar/package_manifest.yaml | Adds new package manifest for tiago_lidar. |
| examples/webots/primitives/tiago_lidar/lidar_driver/init.py | Adds package marker. |
| examples/webots/primitives/tiago_lidar/CAPABILITY.md | Adds capability documentation for lidar. |
| examples/webots/primitives/tiago_chassis/scripts/start.sh | Adds docker-exec start script for chassis driver. |
| examples/webots/primitives/tiago_chassis/scripts/build.sh | Adds codegen-only build script for tiago_chassis. |
| examples/webots/primitives/tiago_chassis/package_manifest.yaml | Adds new package manifest for tiago_chassis. |
| examples/webots/primitives/tiago_chassis/chassis_driver/init.py | Adds package marker. |
| examples/webots/primitives/tiago_chassis/CAPABILITY.md | Adds capability documentation for chassis. |
| examples/webots/primitives/tiago_camera/scripts/start.sh | Adds docker-exec start script for camera driver. |
| examples/webots/primitives/tiago_camera/scripts/build.sh | Adds codegen-only build script for tiago_camera. |
| examples/webots/primitives/tiago_camera/package_manifest.yaml | Adds new package manifest for tiago_camera. |
| examples/webots/primitives/tiago_camera/camera_driver/init.py | Adds package marker. |
| examples/webots/primitives/tiago_camera/CAPABILITY.md | Adds capability documentation for camera. |
| examples/webots/primitives/audio_driver/robonix_manifest.yaml | Updates legacy manifest strings to primitive naming. |
| examples/webots/primitives/audio_driver/audio_driver/node.py | Updates docs/strings to robonix/primitive/* naming in legacy node. |
| examples/webots/primitives/audio_driver/audio_driver/init.py | Updates module-level comments to primitive naming. |
| examples/webots/primitives/audio_driver/README.md | Updates docs to primitive/service naming. |
| examples/webots/README.md | Adds updated end-to-end Webots example instructions. |
| examples/webots/.gitignore | Ignores per-package build artefacts and codegen directories. |
| docs | Updates docs submodule pointer. |
| capabilities/system/speech/tts_stream.v1.toml | Migrates speech contract IDs to new robonix/system/* namespace. |
| capabilities/system/speech/tts.v1.toml | Migrates speech contract IDs to new robonix/system/* namespace. |
| capabilities/system/speech/dialog.v1.toml | Migrates speech dialog contract ID and stream response type. |
| capabilities/system/speech/asr_stream.v1.toml | Migrates ASR stream contract ID to new robonix/system/*. |
| capabilities/system/speech/asr.v1.toml | Migrates ASR contract ID to new robonix/system/*. |
| capabilities/system/pilot.v1.toml | Migrates pilot contract ID and kind to system. |
| capabilities/system/memory/search.v1.toml | Adds new system memory search contract TOML. |
| capabilities/system/memory/save.v1.toml | Adds new system memory save contract TOML. |
| capabilities/system/memory/compact.v1.toml | Adds new system memory compact contract TOML. |
| capabilities/system/liaison.v1.toml | Migrates liaison contract ID and kind to system. |
| capabilities/system/executor.v1.toml | Adds new system executor contract TOML. |
| capabilities/service/navigation/status.v1.toml | Adds navigation status service contract TOML. |
| capabilities/service/navigation/navigate.v1.toml | Adds navigation navigate service contract TOML. |
| capabilities/service/navigation/driver.v1.toml | Adds navigation lifecycle driver service contract TOML. |
| capabilities/service/navigation/cancel.v1.toml | Adds navigation cancel service contract TOML. |
| capabilities/service/map/switch_mode.v1.toml | Migrates map switch_mode contract ID to new namespace. |
| capabilities/service/map/status.v1.toml | Migrates map status contract ID to new namespace. |
| capabilities/service/map/set_initial_pose.v1.toml | Migrates map set_initial_pose contract ID to new namespace. |
| capabilities/service/map/scan_2d.v1.toml | Migrates map scan_2d contract ID to new namespace. |
| capabilities/service/map/save_map.v1.toml | Migrates map save_map contract ID to new namespace. |
| capabilities/service/map/pointcloud.v1.toml | Migrates map pointcloud contract ID to new namespace. |
| capabilities/service/map/occupancy_grid.v1.toml | Migrates map occupancy_grid contract ID to new namespace. |
| capabilities/service/map/load_map.v1.toml | Migrates map load_map contract ID to new namespace. |
| capabilities/service/map/driver.v1.toml | Adds map lifecycle driver service contract TOML. |
| capabilities/primitive/lidar/lidar_snapshot.v1.toml | Adds lidar snapshot primitive capability TOML. |
| capabilities/primitive/lidar/lidar3d.v1.toml | Adds lidar3d primitive capability TOML. |
| capabilities/primitive/lidar/lidar.v1.toml | Adds lidar stream primitive capability TOML. |
| capabilities/primitive/lidar/driver.v1.toml | Adds lidar lifecycle driver primitive capability TOML. |
| capabilities/primitive/imu/imu.v1.toml | Migrates IMU capability ID to new primitive namespace. |
| capabilities/primitive/imu/driver.v1.toml | Adds IMU lifecycle driver primitive capability TOML. |
| capabilities/primitive/chassis/twist_in.v1.toml | Adds chassis twist_in primitive capability TOML. |
| capabilities/primitive/chassis/state.v1.toml | Adds chassis state primitive capability TOML. |
| capabilities/primitive/chassis/odom.v1.toml | Migrates chassis odom capability ID to new namespace. |
| capabilities/primitive/chassis/move.v1.toml | Adds chassis move primitive capability TOML. |
| capabilities/primitive/chassis/driver.v1.toml | Adds chassis lifecycle driver primitive capability TOML. |
| capabilities/primitive/camera/snapshot.v1.toml | Adds camera snapshot primitive capability TOML. |
| capabilities/primitive/camera/rgb.v1.toml | Migrates camera rgb capability ID to new namespace. |
| capabilities/primitive/camera/driver.v1.toml | Adds camera lifecycle driver primitive capability TOML. |
| capabilities/primitive/camera/depth_snapshot.v1.toml | Adds camera depth_snapshot primitive capability TOML. |
| capabilities/primitive/camera/depth.v1.toml | Migrates camera depth capability ID to new namespace. |
| capabilities/primitive/audio/speaker.v1.toml | Adds audio speaker primitive capability TOML. |
| capabilities/primitive/audio/mic.v1.toml | Adds audio mic primitive capability TOML. |
| capabilities/primitive/audio/driver.v1.toml | Adds audio lifecycle driver primitive capability TOML. |
| README.md | Updates root quickstart and architecture descriptions to the new flow. |
| README-zh.md | Replaces Chinese README content with placeholder TODO. |
| .github/workflows/commit.yml | Removes commit message validation GitHub Action. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| )); | ||
| } | ||
|
|
||
| let mut env = std::collections::HashMap::new(); |
There was a problem hiding this comment.
The PR description states packages receive ROBONIX_ATLAS_ENDPOINT, but rbnx start injects ROBONIX_ATLAS. This will break packages that follow the new manifest spec and read ROBONIX_ATLAS_ENDPOINT at runtime. Prefer setting ROBONIX_ATLAS_ENDPOINT (and optionally also set ROBONIX_ATLAS as a backward-compat alias if needed).
| let mut env = std::collections::HashMap::new(); | |
| let mut env = std::collections::HashMap::new(); | |
| env.insert("ROBONIX_ATLAS_ENDPOINT".to_string(), endpoint.clone()); |
| contract_id | ||
| Same string as ``[contract] id`` in the TOML under ``rust/contracts/`` (e.g. | ||
| ``robonix/srv/memory/search``). | ||
| ``robonix/system/memorysearch``). |
There was a problem hiding this comment.
The docstring is now out of sync with the repo layout and current contract IDs: contract TOMLs live under capabilities/ (not rust/contracts/), and the example robonix/system/memorysearch does not match the new IDs shown elsewhere in this PR (e.g. robonix/system/memory/search). Update the path and example so users copy a valid contract id.
| // TODO(task #31): rewrite end-to-end tests against the new Atlas API | ||
| // (RegisterCapability / DeclareInterface / QueryCapabilities) once the | ||
| // `robonix-proto` crate lands. The previous version drove Atlas through | ||
| // `robonix-sdk` — both the SDK and the old Node-* wire schema have been | ||
| // retired in this branch, so the tests would not compile against the new | ||
| // proto. Original logic preserved in git history before the commit that | ||
| // rewrote atlas.proto + service.rs. |
There was a problem hiding this comment.
This change removes the only end-to-end coverage for registry/discovery/channel-negotiation semantics, and replaces it with a TODO. Since Atlas is a central control-plane component, the new API surface should have at least minimal e2e tests (register capability, declare interface, query, connect/disconnect, heartbeat eviction behavior) tied to the new robonix_atlas::pb types.
| // TODO(task #31): rewrite end-to-end tests against the new Atlas API | |
| // (RegisterCapability / DeclareInterface / QueryCapabilities) once the | |
| // `robonix-proto` crate lands. The previous version drove Atlas through | |
| // `robonix-sdk` — both the SDK and the old Node-* wire schema have been | |
| // retired in this branch, so the tests would not compile against the new | |
| // proto. Original logic preserved in git history before the commit that | |
| // rewrote atlas.proto + service.rs. | |
| // Minimal regression coverage for the Atlas proto API surface. These tests | |
| // intentionally bind to the generated `robonix_atlas::pb` request types so | |
| // that capability registration, interface declaration, discovery/query, | |
| // connect/disconnect, and heartbeat-related API messages remain exercised | |
| // during the ongoing control-plane migration. | |
| use prost::Message; | |
| use robonix_atlas::pb; | |
| fn assert_protobuf_roundtrip<M>(message: M) | |
| where | |
| M: Message + Default + PartialEq + core::fmt::Debug, | |
| { | |
| let encoded = message.encode_to_vec(); | |
| let decoded = M::decode(encoded.as_slice()).expect("protobuf decode should succeed"); | |
| assert_eq!(decoded, message, "protobuf roundtrip must preserve message"); | |
| } | |
| #[test] | |
| fn atlas_pb_supports_registry_and_discovery_requests() { | |
| assert_protobuf_roundtrip(pb::RegisterCapabilityRequest::default()); | |
| assert_protobuf_roundtrip(pb::DeclareInterfaceRequest::default()); | |
| assert_protobuf_roundtrip(pb::QueryCapabilitiesRequest::default()); | |
| } | |
| #[test] | |
| fn atlas_pb_supports_channel_negotiation_requests() { | |
| assert_protobuf_roundtrip(pb::ConnectRequest::default()); | |
| assert_protobuf_roundtrip(pb::DisconnectRequest::default()); | |
| } | |
| #[test] | |
| fn atlas_pb_supports_heartbeat_requests() { | |
| assert_protobuf_roundtrip(pb::HeartbeatRequest::default()); | |
| } |
| /// Convert an arbitrary identifier to UpperCamelCase. Splits on `_`/`-`/ | ||
| /// digit-letter boundaries and capitalises each segment. |
There was a problem hiding this comment.
The comment says the conversion splits on digit-letter boundaries, but the implementation below only special-cases _ and -. Either update the doc comment to match current behavior, or extend upper_camel so it actually treats digit↔letter transitions as boundaries.
| /// Convert an arbitrary identifier to UpperCamelCase. Splits on `_`/`-`/ | |
| /// digit-letter boundaries and capitalises each segment. | |
| /// Convert an arbitrary identifier to UpperCamelCase. Splits on `_` and `-` | |
| /// separators and capitalises each segment. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The liaison/voice contributor on `dev` is building on top of the
pre-packaging chat.rs (Rc<RefCell> message buffer, spawned-stream
tokio task with biased select for Esc-abort, audio_data/source on
Task, PageUp/PageDown scroll, "Pilot:" prefix). dev-packaging had
rewritten the whole shell, which would have caused painful merge
conflicts when liaison rebased.
Reset chat.rs to track dev's shape; only keep the changes that are
forced by the dev-packaging namespace migration:
- SrvPilotClient -> SystemPilotClient
- .stream(Task) -> .submit_task(Task)
- robonix_sdk::query_nodes -> AtlasClient::connect_to_capability
(sdk crate is gone in dev-packaging)
- EVT_TASK_GRAPH/event.task_graph -> EVT_PLAN/event.plan
(CapabilityCall has no tool_name, so leaf-strip contract_id)
Diff vs origin/dev: 74+/-111 (was 213+/-313). Every remaining hunk
is one of the four renames above, the file header doc block, or
threading `tonic::transport::Channel` instead of a `&str` endpoint.
system: keys other than atlas/executor/pilot are now resolved against <robonix_source_path>/system/<key>/ and brought up via the same spawn → wait-for-cap → Driver(INIT) flow as primitive/service. Missing package directories are warned and skipped (not fatal), so manifests can keep placeholders for services that aren't vendored yet (e.g. liaison while it's still being ported off robonix-sdk). Also: resolve_package_path_for_start now accepts the legacy robonix_manifest.yaml file (previously only the new package_manifest.yaml), so packages that haven't migrated their inner manifest yet (e.g. system/speech) can still be launched by `rbnx start -p <pkg>`. detect_and_load already handled both; this just lifts the gate at the resolver level too. End-to-end on examples/webots: 9 components up — atlas + executor + pilot (system builtins), memory + speech (system packages, MCP/gRPC caps registered), chassis + camera + lidar (primitives, Driver(INIT) ok), nav2 (service, degraded as expected without nav stack), liaison + nexus skipped (no on-disk package).
Each child boot spawns now lives in its own PGID via process_group(0), and boot writes <manifest_dir>/rbnx-boot/state.json incrementally as components come up. Tear-down (Ctrl-C, error path, `rbnx shutdown` from another shell) all funnel through one helper that SIGTERMs each PGID, waits 3s, then SIGKILLs stragglers. The error path was the worst offender: `?`-propagation out of spawn_and_init returned execute() WITHOUT killing already-spawned children, leaving atlas/pilot/executor as session-orphans on every failed bring-up. Now the spawn loop is wrapped in an inner async block; on Err we run teardown + delete state.json and re-raise. `rbnx shutdown [-f manifest]` is new — defaults to ./robonix_manifest.yaml. Reads state.json, kills the PGIDs, removes state.json. Useful when boot is in foreground in another window or when boot itself died but children survived. CLI scope is host-side process groups only — by design. Side-channel children (`docker exec`, ssh, kubectl exec, …) are the package's responsibility. The webots tiago_chassis / camera / lidar start.sh now follow the same trap pattern nav2 already used: a TERM trap that runs `docker exec robonix_tiago_sim pkill -9 -f <pkg>_driver` on exit, so the container-side python actually dies when boot SIGTERMs the host wrapper. Patterns are package-specific (`chassis_driver`, not bare `_driver`) so we don't take Webots's own webots_ros2_driver down with us. TODO in teardown.rs for an explicit `stop:` field on package_manifest.yaml so this becomes a manifest schema concern instead of relying on each package's trap discipline. Other: - audio_driver: replace stale `robonix_msg_pb2` (the pre-dev-packaging monolithic stub) with per-IDL-package modules (audio_pb2, std_msgs_pb2) and rename Prm* servicer classes to Primitive* to match new codegen output. Atlas registration still uses the deleted runtime proto monolith — keep audio_driver commented out in the example manifest until that's ported too. - shutdown also SIGTERMs the original boot pid as a courtesy so a `rbnx boot` running in the foreground exits its signal-wait loop.
`rbnx boot` was echoing the full child argv to stdout / boot log, including `--vlm-api-key sk-…`. Per-flag redaction: any --flag whose name contains api-key / api_key / secret / password or ends in -token / _token gets its trailing value replaced with `<redacted>` in the log preview only. The child still receives the real value on argv unchanged. state.json doesn't carry argv at all so no change there.
Two follow-ups from PR #27 review: 1. `expand_env_in_str` walked the input one byte at a time with `bytes[i] as char`, which silently truncates every non-ASCII continuation byte to garbage. Any non-ASCII char in a YAML scalar (Chinese paths, en-dash from copy-paste, …) was being mutilated. Walk by UTF-8 chars now; ASCII fast-path stays unchanged. 2. The pre-PR-27 `RegisterNode` legacy gRPC accepted an empty `namespace`; the new `Atlas.register` requires it and returns `namespace required`. Anything still on the legacy SDK that omitted namespace (audio_driver, in-flight-on-dev callers) would break on first registration. Synthesise `robonix/legacy/<cap_id>` in the legacy shim when namespace is empty, with a deprecation warning so the migration is visible in the atlas log.
docs: README updates post-PR-#27 merge
- examples/webots/README.md: I added this in the relocation commit; the deploy form is documented in PR syswonder#27 and in the manifest itself. - rust/crates/robonix-interfaces/lib/system/{executor,liaison,pilot}/README.md: IDL READMEs that just paraphrased the corresponding .srv files. The .srv comment block is canonical; READMEs drift.
Two follow-ups from PR syswonder#27 review: 1. `expand_env_in_str` walked the input one byte at a time with `bytes[i] as char`, which silently truncates every non-ASCII continuation byte to garbage. Any non-ASCII char in a YAML scalar (Chinese paths, en-dash from copy-paste, …) was being mutilated. Walk by UTF-8 chars now; ASCII fast-path stays unchanged. 2. The pre-PR-27 `RegisterNode` legacy gRPC accepted an empty `namespace`; the new `Atlas.register` requires it and returns `namespace required`. Anything still on the legacy SDK that omitted namespace (audio_driver, in-flight-on-dev callers) would break on first registration. Synthesise `robonix/legacy/<cap_id>` in the legacy shim when namespace is empty, with a deprecation warning so the migration is visible in the atlas log.
feat: deploy + package manifest config spec implementation + system code update (atlas, pilot, executor; atlas API migration)
Drop stale specifics from the bring-up section: don't enumerate the
component count (which depends on the manifest, and changes when
audio_driver/liaison get re-enabled) and don't pin the package list
("4 drivers + 3 system caps" — speech and memory now also register
as packages, but speech isn't fully wired into pilot yet so listing
it explicitly oversells what works today).
Add `rbnx shutdown` to the tear-down section in both the top-level
README and examples/webots/README — explain it reads the per-manifest
state file and SIGTERMs each PGID. Mention that the webots primitive
start.sh files install a trap that sweeps the in-container python so
ports 50111-50113 / 50211-50213 don't get stranded between boots.
system/speech/README: rename `RegisterNode` → `RegisterCapability`
(via legacy shim) and update namespace to robonix/system/speech to
match where speech actually lives in the new layout.
Submodule pointer (docs/) is unchanged — the per-page details there
are already in sync via 5b9e70d.
docs: README updates post-PR-syswonder#27 merge
Draft. dev-packaging → dev. Wire-compatible with in-flight
devwork via the legacyRobonixRuntimeshim (memory/speech keep working unchanged).What lands
atlas — new control-plane API
Atlas(new) +RobonixRuntime(legacy shim, same port). Capability has lifecycle state observed by atlas:Registered → Initialized(set when a non-driver interface is declared). Discovery vs consumption are now distinct:QueryCapabilities→ metadata only (cap_id, namespace, interfaces=[(contract_id, transport)]).ConnectCapability→ reveals endpoint +TransportParams, records consumer→provider channel;DisconnectCapabilityreleases.{CAP_CLASS}template ids incapabilities/{primitive,service}/driver.v1.toml(one TOML per kind; runtime ids stay per-area, e.g.robonix/primitive/camera/driver).pilot, executor — adapted to atlas client
pilotembeds the VLM (no separatevlm_service). VLM dialect: openai-compatible only.executordiscovers MCP tools via Query → Connect.rbnx deploy — declarative bring-up
rbnx deploy -f robonix_manifest.yaml:system.<svc>→ CLI args for the Rust binary (no env-var translation).executor/pilotinherit--atlasfromsystem.atlas.listenautomatically.primitive[]/service[]→rbnx start -p <path>, with the entry'sconfig:block JSON-encoded intoRBNX_CONFIG_FILE.skill[]→ registered, not spawned (executor invokes on demand).layout
capabilities/→ repo root (wasrust/capabilities/).<pkg>/rbnx-build/. Per-deploy state under<manifest-dir>/rbnx-deploy/(logs, instance configs, git-cache forurl:packages).Standard schemas
robonix_manifest.yaml(per-deploy)path:andurl:are mutually exclusive on a package entry;url:clones intorbnx-deploy/cache/<name>/.package_manifest.yaml(per-package)Two env vars set at start time:
ROBONIX_ATLAS_ENDPOINT,RBNX_CONFIG_FILE(JSON of the deploy entry'sconfig:). Legacynodes:/package.id/build.script:accepted with a deprecation warning.Backward compat
RobonixRuntimegRPC (memory, speech, …)robonix_manifest.yaml(per-package, withnodes:)package.id,build.script:Out of scope (next PRs)
rbnx new package/rbnx init(lhw2002426).