AgentCanvas / Pages / Developer Guide / Core / Roadmap
Five buckets of open work: TODO (maintenance, refactors, docs) · Feature (new platform capabilities, prefix F) · Env (environment/simulator integration, prefix E) · Method (research method ports, prefix M) · Planned / Deferred. Click a chip to filter; IDs are stable and never reused.
show All TODO Feature Env Method Planned Deferred Hide done

§1TODO

Active maintenance, refactors, and docs work. IDs are stable numeric (highest assigned: 75) and never reused; completed items move to roadmap-done.

63ToolEQA SR metric completeness + convergence fallback
First monolith smoke (3 ep) exposed a metric/termination gap in tooleqa__step + workspace/graphs/eqa/unverified/tooleqa_hmeqa.json: episodes where the 3B agent never calls final_answer run to the env step-limit and emit no metrics port, so they're silently excluded from the SR aggregate (harness reported 1.0 = 1/1 metric-producing episode, true result 1/3). Three fixes: (1) the max-iter fallback keys off env step_index, which only advances on go_next — switch it to the ReAct step count so non-terminating episodes still emit a (failed) answer + metrics; (2) metrics.num_steps reports 0 — populate with the ReAct step count; (3) count env-done-without-answer episodes as success=0 rather than dropping them — partially covered since 2026-06-11: the batch runner now flips an eval episode with no graphOut snapshot to status=error (no more silent exclusion), but emitting an explicit failed verdict is still the right fix. Also investigate why the 3B keeps exploring instead of answering (prompt/format adherence vs upstream 7B).
6NavGPT-CE completion
Nodes + graphs exist (workspace/nodesets/method/navgpt.py + navgpt_mp3d_tools.py, graph JSONs); remaining: agent manifest, skills module, consolidate two graph variants, clean organization (merged with former #27).
17Find a VLN/EQA method that needs states
Verify the functionality of our future state container system — identify a published agent architecture whose reasoning loop requires shared persistent state across steps. Implement as graph preset using state containers to validate the dual-wire design before broader rollout.
Update 2026-04-24 HM-EQA (E6) was a strong candidate — TSDFPlanner + per-step VLM score history fits exactly. Findings: live TSDFPlanner objects cannot cross the server-mode JSON IPC boundary (numba-JIT'd volumes aren't pickle-friendly), so explore-eqa nodeset ended up with subprocess-local module globals keyed by episode_id instead of state containers. Load-bearing design constraint for future container work — the container system needs an in-subprocess addressing mode.
Update 2026-06-15 Cross-process container access normalized (Move 2 of the server-mode transport work): the 0fd2df90 broker + RemoteContainerProxy prototype graduated — broker forwarding made async, the container payload path switched to msgpack, the executor callback URL now resolves from Settings (config.resolve_executor_url) instead of a hardcoded localhost:8000, and silent injection / batched-grant drops now raise loudly. Residual: cross-nodeset access to a replicated nodeset's containers under worker_count>1 is still not routable (the home registry resolves an untagged URL; the broker can't disambiguate N executors in the eval topology) — surfaced as a load-time warning in registry._check_container_ownership rather than a silent 404, so #17 stays open for that piece. Indirectly validated by a 10ep/10-worker explore-eqa run (SR 0.4 ≈ 0.42 baseline; explore_eqa_tsdf_map is owner-local so unaffected).
23Refactor loop control logic
Low priority. LoopRunner is 115 lines with clean separation; GraphExecutor well-structured. Revisit only if bugs surface in IterIn/IterOut, pause/resume/stop lifecycle.
25Refine state container system
in-memory checkpoint wiring at IterOut (done), checkpoint/restore API (done), migrate HistoryTrackerNode/LLMCallNode/NavGPTReasonNode to graph_state (done), NavGPT-CE graph JSON with graph_state (done), state config panel in the frontend (done — StatePanel.tsx: add/remove named states, reducer type). Remaining: decompose HistoryTracker into composite graph with visible state, implement container checkpoint persistence to disk.
28Frontend–backend alignment
Current frontend logic is fully Claude-generated; audit canvas, API client, store, and node types against actual backend endpoints and models to fix any drift or assumptions.
37Refactor the built-in node
40Codebase folder architecture refactor
Audit and reorganize the top-level directory layout for clarity and long-term scalability: review workspace/ subdirectory conventions, evaluate whether agentcanvas/backend/app/ structure scales, align folder naming with documented concepts. Define a canonical layout doc.
Update 2026-07-15 — workspace half done workspace/nodesets/ is now organized by kind (common/ env/ method/ model/ policy/ other/) with placement/naming rules codified in the nodeset-layout standard; graphs are bucketed {vln,eqa,vla}/{verified,unverified}/; codebase-map serves as the backend layout reference. Remaining: the agentcanvas/backend/app/ scaling audit + a decision on the residual top-level dirs.
44Extract VLN-method domain abstractions
Custom method nodes are proliferating (NavGPT/MapGPT/DiscussNav each ship their own parse/format/reason trio), making the canvas look like ordinary visualization over one-off code. Framework abstractions are solid; the domain layer is thin. Candidates: parameterized reasoning turn (prompt template + LLM + structured parser), history formatter with pluggable strategies (full / last-k / summarized), action-space adapter (discrete panoramic ↔ continuous waypoints), observation encoder (pose + RGB + depth → text/image bundle). Do the extraction pass against NavGPT + MapGPT + DiscussNav + VLN-Zero concretely.
45Rich ui_config audit for nodeset tools
Follow-up to #10. Every node in habitat.py, sam.py, policy_cma.py, others.py currently only sets NodeUIConfig(color="..."). Audit which nodes should expose inline editable config_fields and display_fields. Per node: color-only is fine, or upgrade to block/strip/viewer layout. One-line classification per node, then edits for ones that need the richer form.
Update 2026-07-15 — config_fields adoption broad, display side untouched 50 of 64 NodeUIConfig-bearing nodeset files now set config_fields (e.g. env_habitat reset: rgb/depth res + max steps; model_sam: 10 fields; the 2026-07-05 FM standardization wave). Remaining: display_fields and custom layout= are used by zero workspace nodesets, and a tail of nodes is still color-only — the per-node classification pass hasn't been done.
46Unify env action space / action mode contract
Different env nodesets expose radically different action ports today (MP3D: dynamic-cardinality discrete viewpoints via viewpoint_id TEXT + cand_vpids_json; Habitat-continuous: 2-vector or Discrete(4) ACTION int; VLN-CE: discrete Discrete(4); future AI2-THOR: parameterized actions with object IDs), which forces every agent graph to be env-specific. Proposal (deferred): every env's reset/step emits a single action_manifest (TEXT, JSON-serialized) with a first-field type discriminator describing the currently valid action set. For now: keep env-native shapes; revisit once a third env nodeset lands and we can see the real minimal common schema.
47Create new connection type for viewer
54Surface error reports from nodes to the canvas
Today errors are silent on two paths: (a) server-mode nodes' _self_log("error", ...) is not carried back over the proxy HTTP response; (b) many built-in tools swallow soft failures into empty default outputs without exposing an error output port.
Update 2026-05-04 ADR-observability-004 (ErrorBus + error_event WS frame + Report tab) shipped Phase 1 — per-node executor catches, graph-level catches, uncaught FastAPI exceptions, HTTP 4xx/5xx, network failures, React crashes now all surface as toasts + Report-tab entries with full traceback/scope. Still missing: (1) server-mode subprocess proxy doesn't yet relay envelopes published inside the child to the parent's bus; (2) no per-node "explicit error output port" pattern + canvas red-ring decoration yet.
Update 2026-06-15 Gap (1) closed (Move 3 of the server-mode transport work): a subprocess→executor push channel landed — server-mode subprocesses POST structured log/error events to /api/internal/events (api/execution/internal_events.py), which republishes them on the ErrorBuserror_event WS frame, so server-node logs/errors now surface on the canvas. Handler exceptions are first-classed (pushed with node/execution scope + traceback via server/event_push.py) instead of being demoted to a swallowed {"error": ...} value; a WARNING+ logging bridge forwards general subprocess logs. Still open: gap (2) — per-node explicit error output port + canvas red-ring decoration.
Update 2026-07-15 — error-port half of gap (2) closed The declared-error-output-port pattern is in the engine (graph_executor._declares_error_output + routing: an undeclared bare error key is convicted as failure, a declared port passes as data; unit-tested) and adopted by real nodesets (env_libero, env_mp3d, model_detany3d, spatialnav). Remaining: the live-canvas red-ring — the error outline exists only in the post-hoc LogCanvasView replay; BlockLayout (the default live renderer) shows no error state.
55Refactor file management mechanism
Scope TBD (likely covers workspace/{graphs,graph_nodes,nodesets,policies} discovery, outputs/eval_runs/ vs data/outputs/{eval,eval_mock,eval_native}/ vs legacy outputs/runs/ JSONL persistence, overlapping ownership with TODO #40 — clarify what "file management" covers before scheduling).
58Fix BatchedInferenceServer cross-event-loop bug
Under worker_count > 1 with a batched=True node hosted in a parallelism="shared" server-mode subprocess, _BatchQueue._delayed_flush raises RuntimeError: got Future attached to a different loop (observed 2026-05-07 on policy_vlnce__predict at K=10; 88/100 episodes returned metrics={}). Workaround in tree: policy_adapter_vlnce__predict.batched = False (node renamed from policy_vlnce__predict in the 2026-07-04 split). Real fix: make _BatchQueue loop-agnostic or per-worker.
59Promote composite to a runtime first-class entity (eliminate flatten_graph)
Current architecture erases composite at compile time via flatten_graph — leaky abstraction. Cleaner: composite stays as a real runtime entity, executor walks composite boundaries natively. Trade-off: flatten = simple executor + complex compile-time transform with leaky patches; composite-native = composite-aware executor + simple boundary resolution. Trigger: third leak shows up, OR multi-scope authoring becomes the dominant pattern. Until then, the conditional-strip middle option keeps things working.
57Refactor install scripts + conda env layout
13 install_*.sh in scripts/install/ plus 11 conda envs on disk. Naming inconsistent (agentcanvas-X vs bare vlnce/hmeqa/vlaws*), env→nodeset mapping implicit, several install scripts duplicate boilerplate. Audit goals: document canonical env↔nodeset table; decide which envs can merge; extract shared install boilerplate; clarify install_all variants or collapse them; align naming.
Update 2026-07-15 — naming + FM consolidation done Scripts are uniformly install_ac_*.sh with envs/ac_*.yaml + .lock files; the shared ac-fm env consolidated the FM-nodeset interpreters (2026-07-05); clean-room install verify passed on a fresh Ubuntu 20.04 host (2026-07-09, PRs #32/#34/#36). Remaining: the documented env↔nodeset table, shared-boilerplate extraction, merge decisions.
60Tutorial per v1 agent form
Each v1 agent form should have its own tutorial page under docs/pages/developer-guide/tutorials/ showing a minimal worked example: DAG / cyclic loop / ReAct hidden / ReAct router / Plan-and-Execute / bounded multi-agent / embodied sense-act / FM pipeline. Canonical list lives in major-versions.html §1 "Agent forms v1 covers".
61Better cancel-loading strategy
Opening a graph synchronously activates its nodesets. The frontend now shows a loading overlay + Cancel, but Cancel only aborts the client fetch — it does not stop the backend load. True hard-cancel is only possible at the process boundary = server mode: a live GPU/CUDA object's ownership is bound to the process that created it and cannot be handed back to the parent, so "load in a subprocess then return the object" fails for GPU models (works only for pure-CPU picklable objects, which rarely load slowly enough to need interrupting). Plan: (a) offload the synchronous BaseServer.start() to a thread, fixing the framework hole where server-mode startup blocks the entire event loop for up to startup_timeout (1800s); (b) run the ensure endpoint as a token-keyed asyncio.Task + add POST /nodesets/ensure/cancel, where server-mode cancel kills the subprocess; (c) auto-promote heavy local nodesets to server mode so they gain hard-cancel; lightweight local stays on client-side fetch abort (soft cancel).
62Refine graph-executor design-doc
First full rewrite of docs/pages/developer-guide/design-docs/graph/graph-executor.html landed 2026-06-08 (Part I concept SVGs + flowchart; Part II verbatim run()/_fire_node/helpers with ◀── annotations). Committed as a checkpoint — refine later: tighten prose, check dark-mode contrast and flowchart arrow routing in-browser, consider trimming the full-verbatim listings if they prove too long to maintain. 2026-06-11 (evening): the listing problem is structurally resolved — Part I split into graph-view/engine-view, §4.5 redrawn around the ready-queue, §4.6 now a turn-by-turn table; Part II slimmed to run() only (_fire_node/helpers read in source) and the listing is synced from source by docs/_lib/_sync_run_listing.py (comments are the annotations; Pygments two-stage highlighting per .claude/standard/code-highlighting.md). Remaining: in-browser pass — dark-mode contrast, flowchart arrow routing, the new GitHub-palette listing.
64Gym-migrated env graphs under-count SR (terminal STOP success dropped at harvest)
After the env-nodeset gym-like refactor (2026-06-09), the per-step evaluate node (triggered by step_*.info) does not fire after the agent's final STOP step — the loop terminates (e.g. parse_response.is_stop → termination) before evaluate re-fires — so the harvested output_port__metrics graphOut holds the pre-STOP value (success=0). Confirmed on smartway_ce (20 ep, gpt-5-mini, step_budget=15, run 20260610_091657): habitat computes success=1.0 on the STOP step (visible in step.info.metrics) but 5/20 episodes that stopped within the 3 m radius (ep 9/10/11/14/17, dist 0.43–2.61 m) recorded success=0 → reported SR 0.000 instead of ≈0.25 (baseline 0.28). Mechanism, not agent regression (nDTW 0.50 ≥ baseline 0.40; oracle 0.40). Structural fix landed 2026-06-11 (two-sided iterOut refactor): the termination node was removed, evaluate moved to the after-loop band fed once at termination via the iterOut's final_stop handle, and the validator now rejects loop-body-fed metrics/success graphOuts — the engine-level regression test asserts the after-loop consumer receives the terminal iteration's value. Remaining work = re-verification: smartway_ce (re-verified — promoted to vln/verified/), straightforward (re-verified 2026-06-28: bit-identical to native CMA, 50-ep SR 0.38 / SPL 0.348 — promoted to vln/verified/); 2026-07-05 wave: smartway_mono_ce (interface-equivalence audit — env wiring handle-identical to re-verified smartway_ce), navgpt_mp3d (dead legacy state edges removed; clean 1-ep smoke 20260705_194214, full metric harvest via final_stop), octo_simpler (clean 1-ep smoke 20260705_193758) — all promoted to verified/; still open: navgpt_ce (llmCall action/summary port drift) and opennav_habitat (llmCall template drift) — the rest of the wave has landed (threestepnav_ce and octo_simpler now sit in verified/; discussnav_mp3d / spatialnav_mp3d are tracked by #73 / M13, not this mechanism bug). See env template.
66policy_adapter_vla Pi0-on-LIBERO graph path has never run on the vendored tree (vendoring debt chain)
Surfaced 2026-06-11 while smoke-testing the gym-migrated vla_policy_libero.json. The graph's ✅-verified datapoint (2026-05-02, 5/5) came from scripts/smoke/smoke_vla_libero.py against the external vlaworkspace; the canvas-graph path through the policy_adapter_vla server has been broken since the 2026-05-04 adapter split. Debt chain, outermost first — first three fixed 2026-06-11: (1) graph checkpoint_path pointed at a deleted vlaworkspace slurm dir → repointed to vendored data/vla_policy/checkpoints/pi0_libero_pytorch; (2) LiberoRobot lost its RobotAdaptor base in the adapter split, so ensure_robot's _find_subclass never matched → re-based; (3) adapters/models/pi0_model.py kept a one-level-short relative import (..models.openpi → resolves to adapters.models.openpi) → ...models.openpi; (3b) ac-vla-policy env had pip nvidia-nvjitlink-cu12 12.5 vs 12.9-era CUDA libs → upgraded to ~=12.9. Remaining blocker: Pi0Policy.__init__ with DEFAULT_KWARGS use_pretrained_weight=True tries a JAX→PyTorch conversion via python -m vlaworkspace.model.convert_jax_model_to_pytorch (module not vendored; cache_dir 'data/models' also doesn't match the vendored layout data/vla_policy/checkpoints/pi0_base_pytorch) — even though the finetuned safetensors is already local. Fix = make the pretrained-base path resolve to the vendored pi0_base_pytorch (or skip base-init when a full finetune checkpoint is given), then smoke vla_policy_libero 1 ep. Companion engine hole (cousin of the silent-episode-completion memory): a server proxy node whose response is {"x": null} gets logged as outputs={} with error=null and the episode "completes" at step_count=0 — three of today's four failures were silent for this reason.
69Method-layer depth base64 encoding is partially redundant with the msgpack transport (env_habitat.encode_depth_raw_base64)
A follow-up cleanup that surfaced from the #67 transport work. env_habitat's panorama path (observe_panorama, env_habitat.py:536-537) emits two depth products per view: depth_base64 (8-bit min-max-normalised PNG via encode_depth_base64, viewer-only, lossy / relative) and depth_raw_base64 (16-bit-millimetre PNG via encode_depth_raw_base64 env_habitat.py:798, metric-faithful, capped at 65.535 m to fit uint16). This base64-PNG encoding is an application-layer contract consumed by downstream method nodesets (opennav_perception._decode_depth_raw, aoplanner._decode_depth_m, smartway_waypoint) — distinct from, and predating, the Move-1 msgpack transport (#67), which can now carry a raw ndarray across the env boundary as a blob ExtType with no base64 at all. Not a bug — kept intentionally because (1) the consumers' wire contract is a base64 string; (2) 16-bit PNG compresses the depth map (a raw float32 480×640 panorama set is large); (3) the "16-bit / mm / cap 65.535 m" metric semantic is carried by the I;16 PNG format choice; (4) it survives the JSON legacy path too. Cleanup question: once the JSON legacy path is retired (transport msgpack-only) and the consumers can be migrated together, depth_raw_base64 could collapse to a raw depth ndarray + a units flag (is_mm/metres), letting msgpack own compression/encoding and removing the bespoke encode_depth_raw_base64 / _decode_depth_raw pair. Scope: weigh the bandwidth win of PNG compression against the simplification before scheduling; relates to #67 (transport contract) and the viewer-vs-metric depth split.
70Multi-machine batch-eval orchestration
Scale batch eval from a single machine to coordinated multi-machine execution (an AgentCanvas open-source prerequisite, from the find-job dependency graph in the personal todo plan). Prerequisite: first map out the current batch-eval implementation (single-machine worker pool / JobScheduler status quo, the 8765–8769 port pool, admission control) before designing cross-machine scheduling.
72Refactor and standardize the support-status page style
The three support-status pages (vln-/eqa-/vla-support-status.html) drifted while being converted onto the VLN two-part template (2026-06-30). Divergences to reconcile: VLN's §1 matrix uses CSS-styled ours-* badge spans plus an Env column, while EQA/VLA use plain-text status glyphs (✅/❓/❌/🅼/⏳/▫); status-badge vocabulary, column schemas (Method · Env · Status · Graph/roadmap), and Part-I section ordering (env / tool / method / datasets / eval-infra / gaps) are not yet uniform. Standardize the three onto one shared shape — likely a .claude/standard/support-status-style.md defining the column conventions + a single badge legend — then normalize all three pages (including VLN) against it.
73Verify the related methods (re-eval the still-unverified graphs)
Drive the remaining {vln,eqa,vla}/unverified/ graphs to a trustworthy end-to-end datapoint (SR/SPL/LLM-Match + run_id) and promote the ones that pass to verified/, the way voxposer_libero_decomposed went (2026-06-30, libero_object 0.88). Overlaps the gym-migration re-verification wave (#64: smartway_ce, smartway_mono_ce, navgpt_ce, opennav_habitat, straightforward, + mp3d/simpler) and the VoxPoser/Pi-0 blockers (#65, #66). Progress 2026-07-15: verified/ now holds 13 graphs — vln 7 (incl. threestepnav_ce, promoted since), eqa 4, vla 2. Still pending a datapoint: discussnav_mp3d (100-ep faithful-config run; the 5-ep smoke is SR 0.2), tooleqa_hmeqa (also gated on #63), vla_policy_libero/_simpler (#66), the two pyslam_* probe graphs; blocked on port drift: navgpt_ce, opennav_habitat (#64), spatialnav_mp3d (M13, SSG CSV). Track progress against the §1 support matrices on the three support-status pages.
74Finish the policy adapter / CanonicalDict standardization
Continuation of the 2026-06-29 policy_adapter_vlnce (then policy_vlnce) refactor (env→canonical standardizes only; instruction tokenize + obs_transforms moved to canonical_to_model; _full_raw_obs leak removed; tokenize_instruction node retired; verified 3-ep bit-identical to baseline). Remaining: (1) explicit typed rgb/depth/instruction ports at the env→adapter boundary — retire the raw_obs ANY passthrough and rewire the obs iterIn/iterOut loop; (2) land the unified canonical.py v0 (flat schema + plain-dict info + action_space; scratchpad draft) as a shared policy module and migrate policy_adapter_vla onto it; (3) verify the RxR / feat path (rxrce + Seq2Seq) — aligned but not eval'd; (4) carry the standardize/process split into policy_adapter_vla. Mental model + done-state in policy-adapters.html §1.2/§8.

§2Feature TODO

Significant new capabilities that change what AgentCanvas can do. Prefix F.

F1Memory nodeset
Implement naive_memory_nodeset in workspace/nodesets/memory.py — 3 canvas nodes: memory_save (persist episode learnings), memory_search (semantic search over saved memories), memory_get (read specific memory entry). Storage: markdown files in workspace/memory/. Search: embedding-based vector similarity. Inspired by OpenClaw's memory system.
F2LLM dynamic tool-calling
Enable LLMCall node to invoke tools (BaseTool instances) via function-calling during reasoning, rather than only using statically-wired graph edges. Inspired by OpenClaw/ReAct pattern. Subsumed by F6 — kept for traceability.
F3Parallel node execution
Enable multiple independent nodes to fire concurrently within a single iteration step, inspired by LangGraph's Pregel superstep model. Nodes with no data dependencies on each other can run in parallel.
F4Export graph as standalone Python agent
Add an "Export" button that compiles a canvas GraphDefinition into a self-contained Python script importing only workspace/ components — no FastAPI, no React, no WebSocket. Enables headless batch eval, single-file sharing, integration into other codebases. Covers: topological code generation, inlining node configs, IterIn/IterOut → Python while-loop, composite flattening, CLI entry point.
Update 2026-07-09 — partially shipped (Graph SDK) graph_to_code() / Graph.to_code() (app/graph_sdk_codegen.py) compiles any GraphDefinition into a self-contained builder script — topological node/edge emission, inlined configs, composite sub-builders, CLI entry — round-trip semantically exact on MapGPT-MP3D. Differs from the original spec: the emitted script rebuilds the graph via the Graph SDK and runs it through the real GraphExecutor (preserving exact loop semantics) rather than transpiling IterIn/IterOut into a bare Python while-loop. The item's motivation (headless batch eval, single-file sharing, codebase integration) is met by the broader Graph SDK capability; the literal while-loop transpile remains open.
F5Eval Phase 2
Partial: ✓ server-mode set_episode()/reset() over HTTP (ADR-server-002), ✓ env reset between episodes (ADR-eval-002 PB), ✓ proxy nodes preserve node_type, ✓ episode replay from persisted step data (Replay page over /api/replay, reading per-episode log.jsonl + assets). Remaining: run comparison UI (side-by-side metrics for 2+ runs — RunHistory is single-select today), WS bandwidth throttling toggle for live viewer images (only the hardcoded 256px/JPEG-q70 thumb cap in wire_types.py exists).
F6LLM-driven execution mode
Add a second execution mode where LLMCallNode runs an inner ReAct-style agent loop: LLM selects tools via function-calling at runtime instead of following static graph edges. Outer graph remains graph-driven; LLMCallNode internally loops (prompt + tools → LLM → tool_call? → execute → append result → loop until text output). Inspired by Claude Code's query() loop and the ReAct pattern. Subsumes F2.
F7Docker server mode for heavy env nodesets
Unblocked by ADR-eval-002 PB — the subprocess.Popen boundary in WorkspaceComponentRegistry._load_nodeset_as_server is now the only spawn site; container spawn is a one-file swap. Provide official Docker images for Habitat and Matterport3D nodesets. Eliminates habitat-sim / CUDA dependency from agentcanvas host; enables cloud deployment; makes the nodeset usable on machines without GPU drivers.
Update 2026-07-15 — first container bridge shipped (pySLAM) model_pyslam runs its GPL-isolated SLAM stack in a rootless-Docker container (self-managed: initialize()docker run via PySlamContainerClient, FastAPI shim inside; Dockerfiles in-tree). The pattern is proven, but it bypasses the framework spawn site and no env nodeset uses it yet. Remaining: route container spawn through _load_nodeset_as_server (or bless self-managed as the pattern) and ship Habitat / MP3D images.
Update 2026-08-13 — mechanism DONE (Container Launch, ADR-server-005) Container spawn now routes through _load_nodeset_as_server: server_image on the nodeset selects ContainerServer (foreground docker run, ro repo mount, stock auto_host — no bridge code). First user model_orbslam3 verified end-to-end (registry load + canvas graph run). Remaining: migrate model_pyslam off its private bridge; ship Habitat / MP3D env images.
Update 2026-08-20 — model_pyslam migrated model_pyslam now declares server_image (default agentcanvas/pyslam:cuda, weights + /opt/out handle drop via server_mounts, PYSLAM_* via server_env); the private _client.py/_server.py bridge (~870 lines) is deleted. Verified: registry load through ContainerServer + pyslam_tum_slam end-to-end on TUM fr1_xyz — first batch-eval datapoint for the graph: ATE RMSE 0.011 m / RPE RMSE 0.006 m over the full 400-pair sequence (run 20260820_113615; the graph also gained numeric ate_rmse/rpe_rmse graphOuts so the eval runner can harvest a verdict). Fix en route: the pyslam image entrypoint now preserves the injected PYTHONPATH across pyenv-activate. Remaining: ship Habitat / MP3D env images.

§3Env TODO

Environment setup, Habitat versions, conda envs, data pipeline, infra tasks. Prefix E.

E3REVERIE dataset + object grounding
Extend Habitat nodeset with bounding box observation and object selection action for REVERIE's navigate-then-localize task. Needs object annotation data overlay on MP3D scenes.
Update 2026-07-15 — discrete side landed env_mp3d loads REVERIE episodes (_load_reverie(): reverie_id + per-viewpoint bbox_path) and the data is on disk (data/mp3d/tasks/REVERIE/, 10,567 BBox JSONs + all four splits). Remaining: a live grounding observation (today only the GT-bbox path is surfaced), an object-selection action, and the continuous-env (Habitat) side.
E4AI2-THOR nodeset
Wrap AI2-THOR simulator as a BaseNodeSet for embodied task benchmarks (ALFRED, TEACh). Different simulator, different action space — good test of our nodeset abstraction.
E8RxR-CE pose-trace preprocessing
Add a script analogous to gen_skybox_rgb_mp3d.sh that generates trajectories.json.gz from raw RxR speaker pose traces. Only needed by VLN-CE's recollect trainer; the main eval path does not consume this.
E10OpenEQA (A-EQA) integration
Deferred follow-up to E9. Active-EQA mode reuses HM-EQA's Habitat manager (HM3D + ScanNet meshes) + an explore-eqa-style termination policy on top of the E9 nodeset, plus a free-text answer-emit action (deferred-by-design via TODO #46 unified action contract).

§4Method TODO

VLN methods, agent architectures, research implementations to build or port. Prefix M.

PortBench v1 VLN 5-set Committed 2026-05-09 in vln-methods §3.2.1: NavGPT-MP3D ✓ + MapGPT-MP3D ✓ + M3 ✓ + M7 ✓ + M8. M3 (DiscussNav) and M7 (AO-Planner) shipped, verified at 100-ep scale, and moved to roadmap-done. M5 / M6 / M4 are kept as architecture-history reference graphs (lit-review) but are not v1 PortBench tasks — single-box transformer ports collapse to one LLMCall node under PortBench rules.
M1NavGPT-CE canvas graph
Complete the LLM-as-reasoning-core implementation (links to TODO #6). GPT-4/Claude as reasoning engine with explicit thought→action traces, caption node, history accumulation via state containers. Not a v1 PortBench task; kept as the "rule-5 under continuous control" probe for v2.
M2MapGPT canvas graph
LLM reasoning + online top-down map construction for global planning. Requires map-building node (occupancy grid from depth), map-query node, LLM spatial reasoning. Tests state containers (map as persistent state). Note: metric-grid / depth-derived variant, distinct from the shipped linguistic-topo-map MapGPT-MP3D used for v1 PortBench.
M4VLN-SIG canvas graph
Sub-instruction grounding with progress tracking. Decomposes instruction into sub-goals, tracks completion per segment. Good test of state containers + IterIn/IterOut loop control. Not a v1 PortBench task (single-box pretraining-objective method).
M5HAMT neural policy
Hierarchical history with multi-scale transformer. Add as a canvas node in workspace/nodesets/policy/ alongside the policy-adapter nodesets. Requires pretrained weights download. Not a v1 PortBench task (single-box trained transformer).
M6DUET neural policy
Dual-scale graph transformer (fine-grained + coarse topology). Add as built-in policy. More complex than HAMT — needs online graph construction from visited viewpoints. Not a v1 PortBench task.
M8InstructNav canvas graph
Zero-shot Habitat-CE with Dynamic Chain-of-Navigation prompt + Multi-Sourced Value Maps fusing landmark / action / trajectory cues into a per-cell value, picked by argmax (Long et al., CoRL 2024). PortBench v1 slot 5 — only non-LLM-direct-action policy in the 5-set; rule-3 + state-container advance-condition probed sharply. New work: value-map fusion node + dynamic CoN prompt template + per-cell argmax policy node.
M9CA-Nav canvas graph
Constraint-aware zero-shot VLN-CE (Chen et al., TPAMI 2025). The instruction is parsed into a sequence of typed constraints (action / scene / object) and each step is selected to satisfy the active constraint set rather than chase the next sub-goal. Two-LLM-call topology — Constraint-Aware Sub-instruction Manager + Constraint-Aware Value Mapper — maps onto a constraint-buffer state container (lifetime="episode" accumulator with an explicit advance rule), a sharp state-container advance-condition probe. New work: constraint-parser node + value-mapper + advance-condition state, wired against the Habitat-CE waypoint surface. Not a v1 PortBench task (post-cut); v2 candidate. Lineage: vln-methods §2.5.14.
M10AgentVLN canvas graph
Latest zero-shot SOTA — POSMDP formalisation with VLM-as-Brain (Qwen2.5-VL-3B) + plug-and-play skill library + cross-space (3D ↔ 2D) representation mapping + Query-Driven Perceptual CoT (Xin et al., 2026.03 preprint). R2R-CE Val-Unseen SR 67.2 / SPL 64.7 / NE 3.88 at 3 B. Hub-and-spoke (brain-router LLM + typed skill nodes) maps cleanly to an AgentCanvas graph. New work: skill-library nodeset + brain/router node + cross-space mapper. Forward-looking — 2026.03 preprint, ceiling not yet peer-reviewed; deferred from v1 PortBench, re-evaluate for v2 after venue acceptance. Lineage: vln-methods §2.5.20.
M13SpatialNav canvas graph
SSG (spatial scene graph) MP3D port — spatialnav_mp3d.json (vln/unverified/) + workspace/nodesets/method/spatialnav.py / ssg.py, code restored + import-verified. PAUSED 2026-06-20: blocked on the upstream SSG category-mapping CSV (unreleased; the standard MP3D category_mapping.tsv would substitute). Known trap: with the mapping data missing, the SSG silently degrades to an empty-graph NavGPT. Remaining before eval: orchestrator LLM params + eval profile. Added retroactively.

Also representative but intentionally not listed: LM-Nav (22.07, 702 cites) — the first "compose 3 foundation models" VLN agent, but it navigates real-world topological graphs, not MP3D / Habitat, so it needs an env nodeset AgentCanvas doesn't ship (out of current scope). Open-Nav already has a graph (opennav_habitat.json, §1 unverified) — tracked under TODO #6 / §1, not as a new M-item.

§5Planned

Backlog of items intentionally not yet on the critical path.

Execution visualization
Per-node status dots that light up as nodes fire, live. The primitives exist — NodeShell has the status-dot component (used by composites), nav_step/viewer_data WS events already flip status for the agent + viewer nodes, and the post-hoc LogCanvasView replays the full firing sequence — but BlockLayout (the default renderer for most nodes) shows no live firing state. Remaining: wire per-node firing events into the live canvas.
Multi-agent outer graph
Fan-out patterns with multiple concurrent loops.

§6Deferred

Explicitly out-of-scope items kept here so we don't keep re-evaluating them.

GenericBlockRenderer template_editor field type
Would allow promptTemplate to drop its custom .tsx (complex: needs variable chip insertion at cursor position).
Composite node auto-generation from selected nodes
Group selection → auto PortIn/PortOut (needs flatten_graph verification).