## Why The `enable-netdebug` backend (ZeroMQ REQ/REP on 9484, PUB on 9485) is the supported way to inspect and drive a running `app` GUI, but the only clients are ad-hoc scripts (`bin/app/script/`) and the `pydrk` library — there is no CLI. Worse, the node-mutation commands (`AddNode`, `LinkNode`, `RemoveNode`, ...) in `bin/app/src/net.rs` were commented out when the central scene-graph registry was removed, so the `pydrk` client methods for them are dead: the server replies `errc=0` with an empty body and the client misparses it. Inspecting and experimenting with the live scene graph currently requires writing a custom Python script for every question. A junior dev learning the wallet UI has no safe, incremental tool to explore nodes, tweak properties, or draw shapes without recompiling the app. ## What Changes - Re-enable wire-level node creation in `bin/app/src/net.rs` against the current path-addressed tree model: - `AddNode` becomes atomic and path-based: payload `(parent_path, name, node_type)` → replies the new `node_id`; the node is created via the existing Rust factories and linked immediately. Supported types for v1: `Layer` and `VectorArt` (the two factories whose pimpls only need `Renderer` + `RedrawTrigger`); other types fail with `PropertyWrongType`-style rejection (see design for error choice). - `RemoveNode` becomes path-based: payload `(node_path)`; unlinks the subtree from its parent and triggers a redraw (existing `Drop` impls clear GPU draw calls). - `ZeroMQAdapter` gains the `Renderer` handle so `Layer::new` / `VectorArt::new` pimpls can be wired for wire-created nodes. - Update `pydrk/api.py` to match: new `add_node(parent_path, name, node_type)` and `remove_node(node_path)` methods; remove the dead id-based client methods that no longer have server arms (`get_info`, `get_parents`, `link_node`, `unlink_node`, `rename_node`, `scan_dangling`, `lookup_node_id`, `add_property`, `unregister_slot`, `lookup_slot_id`). - Fix `pydrk` `get_method()` result parsing: the server sends `Option>` but the client decodes a bare array, which misparses any method that declares a result. - Add a `pydrk` CLI (`python -m pydrk ...` via a new `__main__.py` + `cli.py`, argparse-based, only dependency stays pyzmq) with subcommands for the whole scene-graph workflow: connectivity check, tree navigation (`ls`, `tree`), display (`props`, `get`), property setting (`set`, type-driven by server metadata, incl. exprs), node creation/removal (`mknode`, `rmnode`), shape data (`set-shape` composing the existing `pydrk.vector_shape` builders), and introspection of signals/methods (`signals`, `methods`, `call`). - Add an interactive shell mode: running `python -m pydrk` with no subcommand drops into a REPL over the same command set, maintaining a current working node path (`cd`, `pwd`) so `ls` lists the current node's children and properties and `set foo XXX` writes a property of the current node. Tab completion (stdlib `readline`) completes command names, node paths (children fetched live from the app), and property names. Non-goals (recorded so they are not silently assumed): no packaging (pyproject/console script) — the CLI runs from `bin/app` like the existing scripts; no new prompt/REPL dependency (prompt_toolkit & co.) — stdlib `readline` only; no rename/link/unlink of pre-existing nodes; no new event/PUB commands; no changes to release builds (`enable-netdebug` stays dev-only). ## Capabilities ### New Capabilities - `pydrk-cli`: Command-line access to a running app's scene graph over the netdebug backend, in one-shot and interactive forms — tree navigation and display, property get/set (typed values and exprs), node creation/removal for Layer and VectorArt, shape data composition, method introspection, and a shell mode with `cd` and tab completion. ### Modified Capabilities (none — `openspec/specs/` is empty; there are no existing main specs to modify.) ## Impact - Rust: `bin/app/src/net.rs` (re-enable + redesign `AddNode`/`RemoveNode`, carry `Renderer`), `bin/app/src/main.rs` (pass renderer into `ZeroMQAdapter::new`), possibly `bin/app/src/error.rs` (only if a new error code is needed — design prefers reusing existing ones). - Python: `bin/app/pydrk/api.py` (new/removed methods, `get_method` fix), new `bin/app/pydrk/cli.py` and `bin/app/pydrk/__main__.py`; existing modules (`serial.py`, `print_tree.py`, `vector_shape.py`, `exc.py`) are reused as-is. - Wire protocol: payloads of commands 1 (`AddNode`) and 9 (`RemoveNode`) change shape. Both sides live in this repo and ship together; the netdebug backend is dev-only (feature-gated out of release builds), so there are no compatibility constraints. - Docs: usage examples live in this change's design; `doc/src/arch/wallet.md` still references the pre-rename `bin/darkwallet/pydrk` path — fixing that is left to a docs change. - No workspace-level `make` targets are affected; Rust verification is `make compile-dev` (and `make compile-apk` for android), Python is run from `bin/app`.