Roadmap
Open work — what's queued, in flight, and explicitly deferred. Done items live in roadmap-done; paradigm-level direction is in major-versions.
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.
§1TODO —
Active maintenance, refactors, and docs work. IDs are stable numeric (1, 2, …, 74 active).
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).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).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.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).installation.md rescued to docs/getting-started-installation-rescued.md (131 lines); remaining: move to active docs, update and verify.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.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.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.docs/pages/developer-guide/core/glossary.html for stale/duplicate/legacy entries, trim verbose definitions, align terms with current ADR state.workspace/nodesets/vlm_backends.py. Closely related to #44._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.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./api/internal/events (api/execution/internal_events.py), which republishes them on the ErrorBus → error_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.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).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..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.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.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.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.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".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).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.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: vln/verified/), vln/verified/); 2026-07-05 wave: smartway_ce), 20260705_194214, full metric harvest via final_stop), 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.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.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).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.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.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.shared (non-replicated) server nodesetgraph_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).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.env_habitat.encode_depth_raw_base64)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.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.{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.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.
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.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.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.query() loop and the ReAct pattern. Subsumes F2.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.
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.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).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.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.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.§4Method TODO —
VLN methods, agent architectures, research implementations to build or port. Prefix M.
LLMCall node under PortBench rules.
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.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.workspace/nodesets/policy/ alongside the policy-adapter nodesets. Requires pretrained weights download. Not a v1 PortBench task (single-box trained transformer).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.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.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.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.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.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.
§6Deferred
Explicitly out-of-scope items kept here so we don't keep re-evaluating them.
GenericBlockRenderer template_editor field type.tsx (complex: needs variable chip insertion at cursor position).flatten_graph verification).