From 7f976d3f8ced1939efbfbd273d65148669685b88 Mon Sep 17 00:00:00 2001 From: rob Date: Fri, 16 Jan 2026 08:02:42 -0400 Subject: [PATCH] Fix hash computation to exclude version/tags metadata The publish dialog adds version and tags fields that aren't stored in the local config.yaml. Exclude these from hash computation so the hash comparison works correctly. Also use Tool.to_dict() for consistent hash computation. Co-Authored-By: Claude Opus 4.5 --- src/cmdforge/gui/pages/tools_page.py | 20 +++++++++++++++++--- src/cmdforge/hash_utils.py | 7 ++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/cmdforge/gui/pages/tools_page.py b/src/cmdforge/gui/pages/tools_page.py index 4633980..64ec6f0 100644 --- a/src/cmdforge/gui/pages/tools_page.py +++ b/src/cmdforge/gui/pages/tools_page.py @@ -46,8 +46,14 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]: if not registry_hash: return ("local", None) - # Compute current hash (excluding hash fields) - current_hash = compute_config_hash(config) + # Compute hash the same way as publish: load Tool object and use to_dict() + # This ensures we compare the same normalized representation + tool = load_tool(tool_name) + if tool: + current_hash = compute_config_hash(tool.to_dict()) + else: + # Fallback to raw config if Tool can't be loaded + current_hash = compute_config_hash(config) if current_hash != registry_hash: return ("modified", registry_hash) @@ -473,15 +479,23 @@ class ToolsPage(QWidget): # Get status from registry status_data = client.get_my_tool_status(tool_name) new_status = status_data.get("status", "pending") + new_hash = status_data.get("config_hash") # Update local config config_data = yaml.safe_load(config_path.read_text()) or {} old_status = config_data.get("registry_status", "pending") + old_hash = config_data.get("registry_hash") + changed = False if old_status != new_status: config_data["registry_status"] = new_status - config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False)) + changed = True + if new_hash and old_hash != new_hash: + config_data["registry_hash"] = new_hash + changed = True + if changed: + config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False)) self.refresh() if new_status == "approved": self.main_window.show_status(f"Tool '{tool_name}' has been approved!") diff --git a/src/cmdforge/hash_utils.py b/src/cmdforge/hash_utils.py index 7bae348..11de7c7 100644 --- a/src/cmdforge/hash_utils.py +++ b/src/cmdforge/hash_utils.py @@ -26,7 +26,12 @@ def compute_config_hash(config: Dict[str, Any], exclude_fields: Optional[list] = Hash string in format "sha256:<64-char-hex>" """ if exclude_fields is None: - exclude_fields = ['published_hash', 'registry_hash'] + # Exclude registry metadata and publication metadata from hash + # These fields are added during publish but aren't part of tool content + exclude_fields = [ + 'published_hash', 'registry_hash', 'registry_status', # registry metadata + 'version', 'tags', # publication metadata (added by publish dialog) + ] # Create a copy without excluded fields config_copy = {k: v for k, v in config.items() if k not in exclude_fields}