mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-28 04:24:51 +02:00
[eric] apps: renaming an app writes the new name into its meta.json so agents stop reading the old one (ENG-308)
This commit is contained in:
@@ -186,6 +186,36 @@ async def read_workspace(workspace_id: str):
|
||||
return {"files": files, "meta": meta, "path": os.path.abspath(folder), "truncated": truncated}
|
||||
|
||||
|
||||
def write_meta_json_fields(workspace_id: str, fields: dict) -> bool:
|
||||
"""Push a rename back onto disk. `meta.json` is what AGENTS read, so a name that only ever
|
||||
reached the record meant the user said "the X app" while the agent saw the old one. Merges
|
||||
into the existing file so nothing else in it is lost, and stays quiet if there is no workspace."""
|
||||
if not workspace_id or not fields:
|
||||
return False
|
||||
folder = os.path.join(WORKSPACE_DIR, workspace_id)
|
||||
if not os.path.isdir(folder):
|
||||
return False
|
||||
meta_path = os.path.join(folder, "meta.json")
|
||||
meta: dict = {}
|
||||
if os.path.exists(meta_path):
|
||||
try:
|
||||
with open(meta_path) as f:
|
||||
loaded = json.load(f)
|
||||
if isinstance(loaded, dict):
|
||||
meta = loaded
|
||||
except (OSError, json.JSONDecodeError, ValueError):
|
||||
meta = {}
|
||||
if all(meta.get(k) == v for k, v in fields.items()):
|
||||
return False
|
||||
meta.update(fields)
|
||||
try:
|
||||
with open(meta_path, "w") as f:
|
||||
json.dump(meta, f, indent=2)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def sync_output_from_meta_json(workspace_id: str, fallback_name: str | None = None) -> bool:
|
||||
"""Sync the Output row's name/description from meta.json (or fallback_name when
|
||||
meta.json has no name). Only overwrites placeholder values; user renames win."""
|
||||
@@ -652,6 +682,11 @@ async def update_output(output_id: str, body: OutputUpdate):
|
||||
if body.thumbnail is not None:
|
||||
output.preview_updated_at = now
|
||||
save(output)
|
||||
# A rename must reach meta.json too, or the UI and the file agents read drift apart (ENG-308).
|
||||
p_sent = body.model_dump(exclude_unset=True)
|
||||
p_meta = {k: getattr(output, k) for k in ("name", "description") if k in p_sent and getattr(output, k)}
|
||||
if p_meta:
|
||||
write_meta_json_fields(getattr(output, "workspace_id", "") or "", p_meta)
|
||||
return {"ok": True, "output": output.model_dump()}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Renaming an app must land in its meta.json (ENG-308, Haik 2026-08-14).
|
||||
|
||||
`update_output` wrote the record and nothing else, so the UI showed the new name while the file
|
||||
AGENTS read still held the old one: the user says "the X app" and the agent is looking at something
|
||||
called something else. The pull direction (`sync_output_from_meta_json`) deliberately only fills
|
||||
placeholders, so nothing was ever going to reconcile them.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from backend.apps.outputs.outputs import WORKSPACE_DIR, write_meta_json_fields
|
||||
|
||||
|
||||
def p_workspace(tmp_name: str) -> str:
|
||||
ws = f"qa-rename-{tmp_name}"
|
||||
folder = os.path.join(WORKSPACE_DIR, ws)
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
return ws
|
||||
|
||||
|
||||
def test_a_rename_lands_in_meta_json():
|
||||
ws = p_workspace("basic")
|
||||
path = os.path.join(WORKSPACE_DIR, ws, "meta.json")
|
||||
with open(path, "w") as f:
|
||||
json.dump({"name": "Old Name", "description": "d", "keepme": 1}, f)
|
||||
assert write_meta_json_fields(ws, {"name": "New Name"}) is True
|
||||
meta = json.load(open(path))
|
||||
assert meta["name"] == "New Name"
|
||||
assert meta["keepme"] == 1, "a rename must not drop the rest of the file"
|
||||
assert meta["description"] == "d"
|
||||
|
||||
|
||||
def test_writing_the_same_name_is_a_no_op():
|
||||
ws = p_workspace("noop")
|
||||
path = os.path.join(WORKSPACE_DIR, ws, "meta.json")
|
||||
with open(path, "w") as f:
|
||||
json.dump({"name": "Same"}, f)
|
||||
assert write_meta_json_fields(ws, {"name": "Same"}) is False
|
||||
|
||||
|
||||
def test_a_missing_workspace_is_quiet_not_fatal():
|
||||
assert write_meta_json_fields("does-not-exist-anywhere", {"name": "x"}) is False
|
||||
assert write_meta_json_fields("", {"name": "x"}) is False
|
||||
|
||||
|
||||
def test_a_garbled_meta_json_is_rebuilt_rather_than_crashing():
|
||||
ws = p_workspace("garbled")
|
||||
path = os.path.join(WORKSPACE_DIR, ws, "meta.json")
|
||||
with open(path, "w") as f:
|
||||
f.write("{not json at all")
|
||||
assert write_meta_json_fields(ws, {"name": "Recovered"}) is True
|
||||
assert json.load(open(path))["name"] == "Recovered"
|
||||
|
||||
|
||||
def test_the_update_route_pushes_renames_to_disk():
|
||||
import inspect
|
||||
from backend.apps.outputs import outputs as mod
|
||||
src = inspect.getsource(mod.update_output)
|
||||
assert "write_meta_json_fields" in src, "a rename that never reaches disk is the whole bug"
|
||||
Reference in New Issue
Block a user