Go2 agentic architecture layers and self-evolution review#2727
Go2 agentic architecture layers and self-evolution review#2727z1050297972-spec wants to merge 39 commits into
Conversation
Greptile SummaryThis PR adds Go2 agentic architecture layers and supporting review artifacts. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (9): Last reviewed commit: "fix(dashboard): resolve Rerun iframe hos..." | Re-trigger Greptile |
write_skill_proposal_file derived the artifact path solely from proposal_id via _safe_filename, then atomically replaced it. A reused stable proposal_id (e.g. improve_navigation) would silently overwrite the earlier review artifact, losing the proposal reviewers were meant to inspect. Mirror the events path's _unique_event_path approach: append -1, -2, ... when the path exists so both artifacts are kept. The author's proposal_id stays verbatim in the JSON; only the filename is disambiguated.
_safe_filename maps every non-alphanumeric character to '_', so
distinct proposal_ids such as skill-v2 and skill.v2 both reduce to
skill_v2 and land on the same path. The prior -N suffix guard only
treated genuine same-id reuses; it would file the second distinct
proposal as skill_v2-1.json, conflating unrelated proposals as
iterations and risking overwrite of the first when ordering changes.
Anchor the stem on an injective digest of the original (un-sanitized)
proposal_id: {slug}-{sha1(id)[:8]}. Distinct ids now always map to
distinct files; the -1/-2 suffix still handles true same-id reuse.
JSON proposal_id stays verbatim. This mirrors how the events path uses
a timestamp prefix to sidestep lossy sanitization.
| payload = json.dumps(normalized, indent=2, sort_keys=True) + "\n" | ||
| tmp_path = path.with_name(f".{path.name}.tmp-{os.getpid()}") | ||
| tmp_path.write_text(payload, encoding="utf-8") | ||
| tmp_path.replace(path) |
There was a problem hiding this comment.
Concurrent Proposals Overwrite The path is chosen with
_unique_proposal_path() before this replace(), but the final write still overwrites if another agent creates the same filename between the exists() check and the replace. Two concurrent calls with the same stable proposal_id can both choose the base file, both return success, and only the later proposal remains in the ledger. The suffix selection needs to claim the final filename atomically, such as with an exclusive create or a lock around path selection and write.
…ct RPC routing Direct tool registration was routing calls to skill.class_name/skill.func_name instead of the module's actual RPCClient.remote_name. When workers register with aliased or remapped names the direct path sends tool calls to a route that does not match the running module, causing failures or empty results.
… hardcoding localhost The iframe src was hardcoded to localhost:9090 and localhost:9876, ignoring DIMOS_RERUN_HOST / --rerun-host and listen_host. When the dashboard is served from a remote machine the embedded Rerun viewer connects to the browser user's localhost and the stream never appears. Read the template at serve time, substitute the correct host and ports (RERUN_WEB_VIEWER_PORT=9878, RERUN_GRPC_PORT=9877).
write_text() directly on the shared JSON state file is not atomic: - a crash during write truncates the file - concurrent agent instances interleave writes, losing transitions Write to a temp file in the same directory, then os.replace() for an atomic swap that guarantees readers always see a complete file.
_load_world_model_state_from_path and InterventionLog.load_dict both cleared in-memory state (transitions, records) before parsing the payload. If any item failed to parse the running model was left corrupted. Build all new objects in temporaries first, swap in only after all parsing succeeds. A bad schema, malformed transition, or float() conversion failure no longer destroys the running model's state.
…k evidence leakage get_recent_outcomes() filtered only by skill_name, so unrelated tasks shared outcome history. When the causal world model recorded a transition without an explicit outcome_json it fell back to _latest_outcome(skill_name), which could grab another task's navigate_with_text success/failure and feed it as evidence for the current task. Add a 'task' dimension to SkillOutcome, record_skill_outcome, and get_recent_outcomes. _latest_outcome now filters by both skill_name and task so that 'go to kitchen' outcomes never leak into 'go to office' predictions.
Auto-recorded outcomes from McpClient._record_tool_outcome were missing the 'task' field, so they were stored with task="". Later, _latest_outcome(skill_name, task="go to the kitchen") filtered by the non-empty task and never matched, causing the causal world model to record transitions with outcome_success=None (unknown). Capture the last user input as _last_user_task and inject it into both _outcome_payload_from_result and _record_tool_exception so auto-recorded outcomes carry the same task key that _latest_outcome filters by.
…nd address The server-side resolved host (listen_host=127.0.0.1 or rerun_host) was used as the iframe target. When the browser is on a different machine: - 127.0.0.1 → browser tries to reach its own loopback - 0.0.0.0 → not a routable address Use window.location.hostname in JavaScript to build the iframe URL client-side, so the browser always connects to the same host it used to load the dashboard page. Ports (9877 gRPC, 9878 web viewer) are hardcoded constants matching the Rerun bridge defaults.
Hi DimOS maintainers,
I am opening this as a draft PR for architecture review and discussion. The goal is to share my current thinking around a Go2 agentic architecture extension, especially around context scheduling, skill-interface evolution, and reviewable self-evolution artifacts. I am not asking for an immediate merge before the design direction is reviewed.
Architecture overview
The main review figure is below. It combines the repo-wide DimOS runtime architecture with the Go2 agentic extension in one diagram, so the agent layer can be reviewed in the context of blueprints, runtime modules, transports, skills, memory, and robot targets.
Editable SVG source:
https://github.com/z1050297972-spec/dimos/blob/refactor/go2-architecture-layers/docs/reviews/2026-07-05-dimos-agentic-architecture.svg
PNG render in the branch:
https://github.com/z1050297972-spec/dimos/blob/refactor/go2-architecture-layers/docs/reviews/2026-07-05-dimos-agentic-architecture.png
Detailed review notes:
What this branch explores
This branch proposes a more explicit Go2 agentic layering model:
The important design boundary is that self-evolution is represented as reviewable artifacts, not automatic code mutation. The agent can record events and proposals, but those artifacts still need human or downstream tooling review before becoming implementation changes.
Main technical changes
unitree_go2_agenticblueprint does not wireWebsocketVisModuledirectly.maininto this branch and preserved upstream infrastructure changes while keeping the local Go2 layered architecture work.Review questions
I would especially appreciate feedback on these points:
ContextProviderboundary the right place to decide which memory/context is effective, with the LLM remaining the main judgment owner after context is selected?Validation performed
mainat6e813a72and resolved merge conflicts while preserving upstream infrastructure updates.git diff --check/git diff --cached --checkon the changed docs, SVGs, and changelog updates during the documentation work.MUJOCO_LOG.TXT.Notes
This is intentionally opened as a draft PR. I expect some parts may need to be split, renamed, or moved to more general DimOS agent packages after maintainers review the architecture direction.