Skip to content

feat(scene): restore stable objects across restarts#78

Open
HeartLinked wants to merge 1 commit into
syswonder:devfrom
HeartLinked:dev
Open

feat(scene): restore stable objects across restarts#78
HeartLinked wants to merge 1 commit into
syswonder:devfrom
HeartLinked:dev

Conversation

@HeartLinked

Copy link
Copy Markdown
Collaborator

No description provided.

@enkerewpo enkerewpo requested review from Copilot and enkerewpo June 9, 2026 12:23
@enkerewpo enkerewpo added enhancement New feature or request module Core module / feature development labels Jun 9, 2026
@enkerewpo enkerewpo added this to the robonix v1.0 milestone Jun 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an object-persistence (“warm restore”) layer to system/scene so stable SceneObjects can survive scene restarts and retain consistent object IDs, with per-map isolation via map_id.

Changes:

  • Introduces a milvus-lite–backed ObjectStore for persisting/loading stable objects (partitioned by map_id).
  • Adds warm-restore plumbing: ObjectRegistry.restore_object(), service boot-time restore + shutdown close, and scene-graph builder upserts on rebuild.
  • Updates perception reconcile to re-bind warm-restored objects to fresh detections (class+pose gating) to prevent ID churn/duplicates; updates docs, scripts, and dependencies accordingly.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
system/scene/tests/test_persistence.py Adds unit tests for restore/rebind behavior and ObjectStore round-trips.
system/scene/scripts/start.sh Mounts host-persisted /data/robonix and wires env vars for object memory + map id.
system/scene/scene_service/state/object_registry.py Adds restore_object() and documents cg_uuid/restored internal attributes.
system/scene/scene_service/service.py Boots with warm restore from ObjectStore, wires embedder, closes store on shutdown.
system/scene/scene_service/scene_graph/builder.py Persists stable objects each rebuild (off-thread) when persistence is enabled.
system/scene/scene_service/persistence.py New milvus-lite persistence implementation with schema management + map scoping.
system/scene/scene_service/ingest/perception_concept_graphs.py Adds embed_text() and rebinds warm-restored objects before minting new IDs.
system/scene/README.md Documents new env vars and “Object memory (warm restore)” behavior.
system/scene/pyproject.toml Adds pymilvus dependency (with version exclusion).
system/scene/docker/requirements.txt Adds pymilvus dependency (with version exclusion) for container builds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +55
def test_restore_object_counter_collision():
"""A new object of a restored class numbers after the highest restored id,
never reusing or colliding with it."""
reg = ObjectRegistry()
reg.restore_object(_make_obj("scene.object.cup_005", "cup"))
reg.restore_object(_make_obj("scene.object.cup_002", "cup"))

new = reg.insert_object(
"cup", Pose3D(0, 0, 0), BBox3D(), 0.5, now=1.0
)
assert new.object_id == "scene.object.cup_006", new.object_id
# Restored objects are still present and addressable.
assert reg.get_object("scene.object.cup_005") is not None
print(" [PASS] test_restore_object_counter_collision")
Comment on lines +58 to +64
def test_restore_object_unparseable_id():
"""An id without a numeric suffix is stored but doesn't touch the counter."""
reg = ObjectRegistry()
reg.restore_object(_make_obj("scene.object.weird", "thing"))
new = reg.insert_object("thing", Pose3D(0, 0, 0), BBox3D(), 0.5, now=1.0)
assert new.object_id == "scene.object.thing_001", new.object_id
print(" [PASS] test_restore_object_unparseable_id")
Comment on lines +42 to +48
def _sanitize_map_id(raw: Optional[str]) -> str:
cleaned = _MAP_ID_UNSAFE.sub("_", (raw or "").strip())
return cleaned or "default"
# open_clip ViT-B-32 text/image features are 512-d (see
# ingest/perception_concept_graphs.py). Object image features already live in
# this space, so caption-text vectors are directly comparable for G3.
_DIM = 512
Comment on lines +195 to +207
def _embed_captions(self, captions: list[str]) -> list[list[float]]:
"""Embed caption strings, falling back to the placeholder vector on a
missing or failing embedder so a write never blocks on the model."""
if self._embed is None:
return [_PLACEHOLDER_VEC] * len(captions)
try:
vecs = self._embed(captions)
except Exception as e: # noqa: BLE001
log.warning("[scene-persist] embed failed, using placeholder: %s", e)
vecs = None
if not vecs or len(vecs) != len(captions):
return [_PLACEHOLDER_VEC] * len(captions)
return vecs
Comment on lines +67 to +78
def test_restore_object_clears_cg_uuid_and_flags_restored():
"""Restore drops the stale per-process concept-graphs uuid and flags the
record `restored`, so the perception reconcile won't evict it before a live
detection re-binds it."""
reg = ObjectRegistry()
obj = _make_obj("scene.object.cup_005", "cup")
obj.attributes["cg_uuid"] = "stale-uuid-from-last-process"
reg.restore_object(obj)
got = reg.get_object("scene.object.cup_005")
assert got.attributes.get("restored") is True
assert "cg_uuid" not in got.attributes
print(" [PASS] test_restore_object_clears_cg_uuid_and_flags_restored")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request module Core module / feature development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants