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 (1, 2, …, 74 active).

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).
19Habitat versions & VLN-CE adaptation
Support multiple Habitat-Sim versions, adapt nodesets/policies for different VLN-CE releases and dataset formats.
22Install instructions
installation.md rescued to docs/getting-started-installation-rescued.md (131 lines); remaining: move to active docs, update and verify.
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). Remaining: decompose HistoryTracker into composite graph with visible state, add state config panel for editing container states in the frontend, 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.
30Align skill and tool concepts with Claude Code patterns
Unclear scope — define concrete deliverables or remove.
32Check asynchronization system
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 (agent/, nodes/, nodesets/, graphs/, graph_nodes/, skills/, servers/, policies/), evaluate whether agentcanvas/backend/app/ structure scales, align folder naming with documented concepts. Define a canonical layout doc.
41Document the pipeline for adding each kind of nodeset and node
Subdivide NodeSets into finer categories (env, ML model, reasoning/tool) and write per-category contributor guide covering scaffolding, registration, UI config, server-mode considerations.
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.
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
48Validate /implement-graph pipeline end-to-end
Exercise all 4 phases (Port → Trace → Nodeset → Graph) on a fresh method to stress-test tutorials + shims.
49Clean up glossary
Audit docs/pages/developer-guide/core/glossary.html for stale/duplicate/legacy entries, trim verbose definitions, align terms with current ADR state.
50Decouple VLM backend from explore-eqa nodeset
v1 ships Prismatic-locked. Once a second EQA method lands or someone asks to run explore-eqa with a non-Prismatic backend, extract a VLM-backend abstraction exposing the three primitives the agent uses: generation, token-likelihood scoring, multi-image prompting. Candidate home: workspace/nodesets/vlm_backends.py. Closely related to #44.
51Refactor auto-layout to handle the new note built-in node
Current topological layering wasn't designed for free-floating annotation nodes.
52Make the Explorer panel resizable like VSCode
Drag handle to adjust sidebar width.
53Debug Batch Evaluate page
UI buttons unresponsive (clicks have no effect); investigate event handlers / API wiring.
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.
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).
56Clarify method vs foundation-model boundary in nodeset design
Codify the principle: foundation-model nodesets (vlm_prismatic, vlm_llava) expose generic, domain-agnostic primitives (score_tokens, generate, embed); method nodesets (explore_eqa, mapgpt) consume those primitives via wires and own all task-specific glue. Currently mixed. Closely related to #44 and #50 — this is the umbrella principle they implement.
Update 2026-07-04 — ✅ resolved Principle codified in .claude/standard/nodeset-layout.md and now enforced codebase-wide: the last method-embedded FMs were extracted to generic wrappers (model_blip2, vlm_spatialbot, model_dinov2, model_ram ram/ram_plus variants; opennav_perception / smartway_perception deleted, navgpt_mp3d_tools stripped of model code). Equivalence: captured-input unit replay (10/10, 12/12 cross-env, DINOv2 bitwise, BLIP-2 3/3) + per-graph 1-episode smokes. Branch refactor/foundation-models. #44 (domain abstractions) remains open. Ready to archive.
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.
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), opennav_habitat (llmCall template drift), + the remaining mp3d/simpler graphs. See env template.
65VoxPoser-LIBERO gripper convention inverted at the actuation boundary
The LIBERO prompts define gripper_map with the upstream RLBench convention: 1 = open, 0 = closed (get_gripper_map_prompt.txt: "open everywhere → gripper_map[:,:,:] = 1"). But the port's actuation layer executes the trajectory grip bit as 1 = closed (env_libero__step_pose: libero_grip = -1.0 if grip_in > 0.5 else 1.0, where robosuite -1 = close; the retired adapter's _execute_to had the same inversion, self-labelled "VoxPoser convention 1.0=closed"). Net effect: every grasp executes mirrored — descend with closed fingers, open inside the object — which only works accidentally on concave objects (bowls, internal-press grasp) and plausibly explains the 9–18% VAS SR ceiling of the retired monolithic line. This inversion predates the 2026-06-10 decoupling (the decoupled smoke 20260610_110252 faithfully reproduces it; grip_seq for "grasp" = approach-closed/open-at-bowl). Fix = flip the bit interpretation at exactly one boundary (step_pose, plus expand_for_settle's C/O labelling) and re-eval voxposer_libero_decomposed; do it together with the unverified-graph re-verification wave (TODO #64). Also note the companion frozen-v1 parity trap already handled in code: plan-time gripper state must stay pinned OPEN (see workspace/nodesets/method/voxposer/_wired_env.py) because LIBERO's reset settle leaves fingers physically closed.
Update 2026-06-28 — ✅ resolved The bit interpretation was flipped at exactly the actuation boundary (env_libero__step_pose now maps grip > 0.5 → LIBERO/robosuite +1 = close; the stale "-1 = close" comments were corrected), alongside two more stacked convention bugs + grasp-geometry fixes. voxposer_libero_decomposed grasps end-to-end and was re-evaled + promoted to vla/verified/ (libero_object SR 0.88, 2026-06-30).
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.
67Server-mode transport contract — opaque (non-JSON-safe) values can't cross the HTTP boundary
A deep one. A server-mode node's outputs are serialised per output-port wire_type via serialize_value (app/server/server_app.py); for ANY (and any non-IMAGE type) this falls through to _make_json_safe, which only converts ndarray / np-scalars / dict / list and passes everything else through unchanged — so torch.Tensor, PIL.Image, bytes, set, custom classes, and numba/JIT objects (e.g. TSDFPlanner) reach the JSON encoder and 500. IMAGE rides base64 PNG; DEPTH + nested ndarrays ride the lossless __ndarray__ marker (depth fixed 2026-06-13). In-process ports have no such limit (by-reference). The question: what is the principled transport contract for server-mode ports? Options — (a) document + enforce "ANY across a server boundary = JSON-safe-after-numpy; opaque objects stay subprocess-local (episode_id-keyed module global, the explore-eqa/TSDFPlanner pattern) or convert to ndarray at the producer (torch→.cpu().numpy(), PIL→ndarray)"; (b) extend _make_json_safe with a registered codec table (torch / PIL / bytes) so common heavy types cross transparently; (c) a handle/pointer wire type for objects that must stay process-local by design. Relates to #17 (state-container in-subprocess addressing) and the silent-episode-completion engine hole (a proxy response {"x": null} logs as outputs={} with error=null). Scope: define the contract first, then decide enforcement vs codec-extension.
Update 2026-06-15 — ✅ resolved Took option (b): a msgpack codec replaced JSON+base64 (serialization.py pack_body/unpack_body; one blob ExtType carries raw bytes for ndarray/torch.Tensor/PIL.Image, bytes ride native msgpack bin). Type-driven with receiver-side degrade (a torch/PIL blob decoded in an env lacking that type comes back as ndarray, never crashing — cross-boundary = cross-env). /call negotiates by Content-Type and still accepts JSON (migration window); the container path is msgpack too. Option (c) — opaque handle wire type — dropped: live objects stay home by design, only passive data crosses. msgpack added to requirements.txt + installed in all 12 server-mode envs. Verified: 63 backend unit tests + a real-subprocess E2E + a 10ep/10-worker explore-eqa run (no 500s; SR 0.4 ≈ 0.42 baseline). Formalized as ADR-server-004. Ready to archive.
68Guardrail: reject/warn a stateful state-container on a shared (non-replicated) server nodeset
From the state-home principle (mutable shared state → executor graph_state/home container; per-worker private state → replicated server; a shared server must be stateless to be safely shared across workers): a shared server owning a mutable nodeset-owned container is an anti-pattern — it forces hand-rolled per-episode isolation (episode_id keying + evict) and races under worker_count>1. state_demo (workspace/nodesets/common/state_demo.py) currently demonstrates exactly this; explore_eqa's pre-f256ba48 keyed container was the same mis-classification (now fixed by splitting the voxel world-model into the replicated explore_eqa_tsdf). Work: (1) decide enforcement strength — load-time hard reject vs warn + docs-discourage — and implement in build_container / the registry nodeset-load path; (2) migrate state_demo to replicated or make it stateless. Deferred from the cross-nodeset container-access GAPS doc (now superseded by the State Containers design doc §9.1); relates to #17 (the cross-process container-access capability that doc proposes).
Update 2026-06-15 — ✅ resolved Took warn enforcement (escalatable to hard-reject later): registry._check_container_ownership logs a [#68] warning at nodeset-load when a parallelism="shared" server owns state container(s); state_demo migrated to parallelism="replicated". Unit-tested (app/test_state_normalization.py). Ready to archive.
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.
71System-log runtime statistics
Add runtime observability to the execution engine (an AgentCanvas open-source prerequisite, from the find-job dependency graph): collect per-node execution time, inter-node data-transfer (wire/transport) latency, etc., to locate bottlenecks.
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-05: smartway_mono_ce, navgpt_mp3d, octo_simpler promoted (see #64); the octo_simpler verified-status discrepancy is resolved (promoted after a clean 1-ep smoke). Still pending a datapoint: discussnav_mp3d (interface-clean, fitness not paper-comparable), tooleqa_hmeqa, vla_policy_libero/_simpler; blocked on port drift: navgpt_ce, opennav_habitat, spatialnav_mp3d. 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.
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. Remaining: run comparison UI (side-by-side metrics for 2+ runs), episode replay from persisted step data, WS bandwidth throttling toggle for live viewer images.
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.

§3Env TODO

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

E2RxR-CE dataset integration (Habitat-CE)
Extend workspace/nodesets/env/env_habitat.py to load RxRVLNCEDatasetV1 via VLN-CE's upstream rxr_cma_{en,hi,te}.yaml configs. Surface ep.instruction.language and an extras dict on episode info (mirroring MP3D's RxR extras schema). Add dataset as a third HabitatEnvPanel field so the cascade becomes dataset → split → episode_index. Out of scope (tracked as E7 / E8): pre-computed BERT instruction features, pose-trace trajectories.json.gz preprocessing.
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.
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.
E6HM-EQA dataset/benchmark integration
Completed 2026-05-09. Env side shipped as workspace/nodesets/env/env_hmeqa/__init__.py (5 tools, free-pose teleport, ac-hmeqa conda env). Method side (paired with explore-eqa agent) shipped as workspace/nodesets/explore_eqa.py + explore_eqa_tsdf.py (6 tools, Prismatic-locked for v1 — see TODO #50). Closed end-to-end on the 100-episode HM-EQA val standard split: SR 0.4300 (n=100/100), ≥ paper 0.38 by +5 pp. Two framework holes patched in the same session (silent navmesh degrade + step_budget==0 silent-complete).
E7RxR-CE BERT instruction features
Download and install the pre-computed rxr_instruction_sensor BERT .npz features keyed by {split}/{id}/{lang}. Only required when running the CMA / Seq2Seq policy baseline on RxR-CE; LLM-driven agent graphs do not need this.
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.
E9OpenEQA (EM-EQA) integration
Completed 2026-05-04. Free-form Embodied QA with LLM-as-judge — first benchmark in the repo to exercise free-text reasoning + external-judge eval. Shipped: workspace/nodesets/env/env_openeqa_em.py (5 tools), three graphs (openeqa_em_blind_llm.json, openeqa_em_single_frame.json, openeqa_em_multiframe.json). End-to-end smoke returned openeqa_score=5.0, openeqa_llm_match=1.0.
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. 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.
M3DiscussNav canvas graph
Multi-LLM discussion architecture where multiple LLM agents debate navigation decisions. PortBench v1 slot 3 (MP3D discrete). 8-expert LLM fan-out + aggregator topology — exercises F3 parallel execution and rule-5 (multi-agent loop). Cost note: ~8 LLM calls per step × N steps × M episodes; budget accordingly.
Update 2026-06-16 — ✅ shipped discussnav_mp3d.json (vln/unverified/) + workspace/nodesets/method/discussnav.py run end-to-end (SR 0.2 on a 5-ep gpt-5-mini smoke) after three fixes: InstructBLIP pinned to the agentcanvas env, caption/RAM gated to navigable views (not all 36), dir_ids alignment. Full eval + promotion tracked by #73.
Update 2026-07-04 — faithful-config verified at 100-ep scale grill-implement iteration 3 matched the release run-config chain (512×512 RGB-D render, horizon 5, GroundingDINO back at the faithful 0.4 threshold — the earlier 0.25–0.35 advice was a 224-px-era artifact); rand100 100-ep run 20260704_011258: SR 0.35 · OSR 0.50 · SPL 0.205 (gpt-5-mini both VLMs; paper zero-shot line SR 25.5, not same-model). Promoted to vln/verified/ 2026-07-04.
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.
M7AO-Planner canvas graph
Zero-shot Habitat-CE pipeline: SAM segments walkable ground → LLM picks waypoints from affordance pixels → 3D path planner traces the route (Chen et al., AAAI 2025). PortBench v1 slot 4 — canonical "compose three foundation models" probe; rule-3 (typed-tool pipeline) signal is sharp. SAM nodeset already shipped; new work: 3D path planner node + LLM affordance-prompt template + integration graph against Habitat-CE waypoint surface.
Update 2026-06-16 — ✅ shipped (M1) aoplanner_ce.json (vln/unverified/) + workspace/nodesets/method/aoplanner/: faithful two-VLM VAP pipeline, pure-FM variant (upstream's entangled supervised TRM dropped), SmartWay-style decider; model_grounding_dino reuses the detany3d env; grounding threshold ~0.25–0.35 (0.4 too high). Full eval + promotion tracked by #73.
Update 2026-07-04 — faithful-config verified at 100-ep scale grill-implement iteration 3 matched the release run-config chain (512×512 RGB-D render, horizon 5, GroundingDINO back at the faithful 0.4 threshold — the earlier 0.25–0.35 advice was a 224-px-era artifact); rand100 100-ep run 20260704_011258: SR 0.35 · OSR 0.50 · SPL 0.205 (gpt-5-mini both VLMs; paper zero-shot line SR 25.5, not same-model). Promoted to vln/verified/ 2026-07-04.
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.
M11SmartWay canvas graph
Waypoint-predictor + MLLM navigator with backtracking, ported as the smartway nodeset family (smartway / smartway_perception / smartway_waypoint / smartway_mono). Shipped & verified: smartway_ce.json promoted to vln/verified/ after the #64 terminal-STOP fix; monolithic variant smartway_mono_ce.json promoted 2026-07-05 via interface-equivalence audit (#73). Added retroactively — the port predated this list.
M12Three-Step Nav canvas graph
Open-Nav-family three-step pipeline (waypoint proposal → VLM scene description → LLM decision), ported as the threestep / threestep2 nodesets + threestep_ce.json / threestep2_ce.json (vln/unverified/). Faithfulness is held by an upstream-import byte harness (82 checks — re-run after any threestep edit). The 2026-07-03 perception-alignment round landed the aligned stack (RGB-1024 knob, DDPPO depth-encoder fix — it had silently never loaded in prior Open-Nav-family runs, waypoint rewrite, JPEG VLM bytes): n=25 gives SR* 0.24 under the paper's no-stop-gate success definition (official stop-gated SR 0.08) — statistically compatible with the paper's 0.34 at n=25. Full eval + promotion tracked by #73. Added retroactively.
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.
Multi-agent outer graph
Fan-out patterns with multiple concurrent agent loops.
SLAM integration
Deferred — waiting on ORB-SLAM bindings.

§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).