Skip to main content
Glama
jewelmorph.py18.7 kB
#!/usr/bin/env python3 """ JEWELMORPH - Phantom gem transmuted to phantom jewel Part of the JEWELSTORM workflow (parallel to QUILLSTORM, but for PHANTOM GEMs). Purpose: - Validates JEWELSTRIKE session via guardian token and witness/guardian copies - Ensures the working_gem has actually been inscribed (changed vs witness_gem) - Strips jewel_id from all nodes in the working_gem - Writes phantom_jewel.json alongside phantom_gem.json (atomic write) - Cleans up working_gem + guardian copy and clears guardian index entry Usage: python jewelmorph.py "<identifier>" "<guardian-token>" identifier can be: - Raw hash (e.g., "6a3f") - Prefixed hash (e.g., "jm-6a3f") - Full prefix (e.g., "jm-6a3f-my-refactor", right-trimmed dashes) - Full working_gem filename (with or without path) - Phantom gem filename (looks up in index to find associated session) Returns JSON to stdout: { "success": true, "hash": "a3f7", "phantom_gem": ".../phantom_gem.json", "phantom_jewel": ".../phantom_jewel.json", "working_gem_deleted": true } """ import json import os import sys import shutil from pathlib import Path from typing import Any, Dict INDEX_FILENAME = "jewelstorm_index.json" def _load_index(index_path: Path) -> Dict[str, Any]: try: if index_path.exists(): with open(index_path, "r", encoding="utf-8") as f: return json.load(f) return {} except Exception: return {} def _build_pj_preview_lines(nodes: list, max_note_chars: int = 1024) -> list[str]: """Build human-readable preview of phantom_jewel tree (PJ-1.2.3 format). Format: [PJ-1.2.3] ⦿ Node Name [note-preview] with 4-space indentation per depth level and truncated note previews. This uses path-based preview_id (not jewel_id) because phantom_jewel.json is a COLD artifact after JEWELMORPH (jewel_ids are stripped). """ # PASS 1: Assign preview_id to all nodes and collect max width all_preview_ids: list[str] = [] def assign_ids(node: dict, path_parts: list[str]) -> None: if not isinstance(node, dict): return path_str = ".".join(path_parts) preview_id = f"PJ-{path_str}" node["preview_id"] = preview_id all_preview_ids.append(preview_id) children = node.get("children") or [] for idx, child in enumerate(children, start=1): if isinstance(child, dict): assign_ids(child, path_parts + [str(idx)]) for root_index, root in enumerate(nodes, start=1): if isinstance(root, dict): assign_ids(root, [str(root_index)]) max_id_width = max((len(pid) for pid in all_preview_ids), default=0) # PASS 2: Build aligned preview lines lines: list[str] = [] def walk(node: dict, path_parts: list[str]) -> None: if not isinstance(node, dict): return preview_id = node.get("preview_id", "") padded_id = preview_id.ljust(max_id_width) # Build one-line preview depth = max(1, len(path_parts)) indent = " " * 4 * (depth - 1) children = node.get("children") or [] has_child_dicts = any(isinstance(c, dict) for c in children) bullet = "•" if not has_child_dicts else "⦿" name = node.get("name") or "Untitled" note = node.get("note") or "" if isinstance(note, str) and note: flat = note.replace("\n", "\\n") if len(flat) > max_note_chars: flat = flat[:max_note_chars] name_part = f"{name} [{flat}]" else: name_part = name lines.append(f"[{padded_id}] {indent}{bullet} {name_part}") # Recurse into children with extended path for idx, child in enumerate(children, start=1): if isinstance(child, dict): walk(child, path_parts + [str(idx)]) for root_index, root in enumerate(nodes, start=1): if isinstance(root, dict): walk(root, [str(root_index)]) return lines def _strip_jewel_ids(obj: Any) -> None: """Recursively remove jewel_id from all nodes in-place.""" if isinstance(obj, dict): if "jewel_id" in obj: obj.pop("jewel_id", None) # Handle both "children" (nodes) and "nodes" (export wrapper) children = obj.get("children") or obj.get("nodes") or [] if isinstance(children, list): for child in children: _strip_jewel_ids(child) elif isinstance(obj, list): for item in obj: _strip_jewel_ids(item) def _build_id_index(obj: Any, index: Dict[str, Dict[str, Any]]) -> None: """Build a mapping from Workflowy id → node for fast metadata lookup.""" if isinstance(obj, dict): nid = obj.get("id") if isinstance(nid, str): index[nid] = obj children = obj.get("children") or obj.get("nodes") or [] if isinstance(children, list): for child in children: _build_id_index(child, index) elif isinstance(obj, list): for item in obj: _build_id_index(item, index) def _restore_per_node_metadata_from_phantom(gem_data: Dict[str, Any], phantom_data: Dict[str, Any]) -> None: """Reattach per-node metadata (completed/data/timestamps) from phantom_gem. The HOT VISIBLE GEM strips these fields for readability and token efficiency, but phantom_gem.json keeps the authoritative values. This function walks gem_data's nodes and, when an id matches a node in phantom_data, copies back the following keys if they are missing: - completed - data - createdAt - modifiedAt - completedAt """ id_index: Dict[str, Dict[str, Any]] = {} _build_id_index(phantom_data, id_index) def _walk(node: Dict[str, Any]) -> None: nid = node.get("id") if isinstance(nid, str) and nid in id_index: src = id_index[nid] for key in ("completed", "data", "createdAt", "modifiedAt", "completedAt"): if key in src and key not in node: node[key] = src[key] for child in node.get("children") or []: if isinstance(child, dict): _walk(child) for root in gem_data.get("nodes") or []: if isinstance(root, dict): _walk(root) def jewelmorph(identifier: str, guardian_token: str) -> dict: """Finalize a JEWELSTORM session, producing phantom_jewel.json. Safety invariants (mirrors QUILLMORPH semantics): 1. Resolve identifier → session (hash, phantom_gem_path, working_gem, witness_gem). 2. Validate guardian_token against stored token (human-in-the-loop authorization). 3. Validate witness_gem vs guardian_gem (no tampering with witness). 4. Ensure working_gem differs from witness_gem (non-empty inscription). 5. Strip jewel_id and atomically write phantom_jewel.json alongside phantom_gem.json. 6. Remove working_gem + guardian copy and clear index entry. """ # Temp + guardian directories (parallel to jewelstrike.py) temp_dir = Path(r"E:\__daniel347x\__Obsidian\__Inking into Mind\--TypingMind\Projects - All\Projects - Individual\TODO\temp") guardian_dir = temp_dir / ".jewel-guardian" index_path = guardian_dir / INDEX_FILENAME index = _load_index(index_path) hash_value = None phantom_gem_path: Path | None = None entry: Dict[str, Any] | None = None # Normalize identifier (strip jm- prefix, right-trim dashes) clean_id = identifier.strip().rstrip("-") if clean_id.startswith("jm-"): clean_id = clean_id[3:] # PHASE 1: Try to interpret identifier as a path potential_path = Path(identifier) if potential_path.exists(): # Working gem in temp dir with jm- prefix if potential_path.parent == temp_dir and potential_path.name.startswith("jm-"): # Extract hash from filename filename = potential_path.name if "--" in filename: prefix_part = filename.split("--")[0] parts = prefix_part.split("-") if len(parts) >= 2: hash_value = parts[1] else: parts = filename.split("-") if len(parts) >= 2: hash_value = parts[1] # Find matching index entry by working_gem if hash_value: for key, val in index.items(): if val.get("hash") == hash_value and val.get("working_gem") == str(potential_path): phantom_gem_path = Path(key) entry = val break else: # Treat as phantom_gem path – look up directly in index gem_key = str(potential_path.resolve()) if gem_key in index: phantom_gem_path = potential_path.resolve() entry = index[gem_key] hash_value = entry.get("hash") # PHASE 2: If still unresolved, treat identifier as hash/prefix if not entry: if not hash_value and len(clean_id) >= 4: if all(c in "0123456789abcdef" for c in clean_id[:4].lower()): hash_value = clean_id[:4].lower() if hash_value: for key, val in index.items(): if val.get("hash") == hash_value: phantom_gem_path = Path(key) entry = val break if not entry or not phantom_gem_path or not hash_value: return { "success": False, "error": f"Could not resolve identifier to active JEWELSTORM session: {identifier}", } # Extract paths from entry working_gem_str = entry.get("working_gem") witness_gem_str = entry.get("witness_gem") human_prefix = entry.get("human_prefix") if not working_gem_str or not witness_gem_str: return { "success": False, "hash": hash_value, "error": "Guardian index entry is incomplete (missing working_gem or witness_gem)", } working_gem_path = Path(working_gem_str) witness_gem_path = Path(witness_gem_str) # Validate working_gem exists if not working_gem_path.exists(): return { "success": False, "hash": hash_value, "error": f"Working gem does not exist: {working_gem_path}", } # Validate guardian token (human-in-the-loop gate) stored_token = entry.get("guardian_token") if stored_token: if not guardian_token or guardian_token.strip() != stored_token: return { "success": False, "hash": hash_value, "error": ( "✅ PRE-MORPH STAGE COMPLETE\n\n" "JEWELSTRIKE session is active, but Guardian Token is required to transmute.\n\n" "**MANDATORY NEXT STEP (HUMAN IN THE LOOP):**\n" "1. Dan reviews the JEWELSTORM changes (e.g., via PARALLAX / Araxis).\n" "2. Dan provides the Guardian Token from the JEWEL guardian index.\n" "3. Re-run jewelmorph with the same identifier and the correct token.\n" ), } # Reconstruct guardian_gem path if human_prefix: guardian_filename = f"{hash_value}-{human_prefix}.witness" else: guardian_filename = f"{hash_value}-gem.witness" guardian_gem_path = guardian_dir / guardian_filename # Validate witness and guardian copies if not witness_gem_path.exists(): return { "success": False, "hash": hash_value, "error": f"Witness gem missing: {witness_gem_path}", } if not guardian_gem_path.exists(): return { "success": False, "hash": hash_value, "error": f"Guardian gem missing (JEWELSTRIKE may have failed): {guardian_gem_path}", } import filecmp # Witness vs guardian must be identical (no tampering with witness_gem) if not filecmp.cmp(str(witness_gem_path), str(guardian_gem_path), shallow=False): return { "success": False, "hash": hash_value, "error": ( "💥 THE GEM FRACTURES! Witness Gem has been altered.\n\n" "Alert: The witness_gem was modified instead of the working_gem.\n" "JEWELMORPH is ABORTED to prevent corrupting PHANTOM JEWEL.\n\n" f"Witness: {witness_gem_path}\n" f"Guardian: {guardian_gem_path}\n" ), } # Ensure working_gem is actually different from witness_gem (non-empty JEWELSTORM) if filecmp.cmp(str(working_gem_path), str(witness_gem_path), shallow=False): # Best-effort cleanup of guardian + working_gem; keep witness_gem as baseline try: if guardian_gem_path.exists(): guardian_gem_path.unlink() if working_gem_path.exists(): working_gem_path.unlink() except Exception: pass return { "success": False, "hash": hash_value, "error": ( "⚠️ THE GEM REMAINS UNTOUCHED. No JEWELSTORM inscription detected.\n\n" "working_gem is identical to witness_gem – no semantic changes were applied.\n" "Either no JEWELSTORM operations ran, or a different file was edited.\n\n" "💡 ACTION OPTIONS:\n" " 1. Apply transform_jewel operations to working_gem and try again, OR\n" " 2. Use jeweldrop.py to collapse this JEWELSTORM session if it is no longer needed.\n" ), } # PHASE 4: Strip jewel_id and write phantom_jewel.json atomically try: with open(working_gem_path, "r", encoding="utf-8") as f: gem_data = json.load(f) except Exception as e: return { "success": False, "hash": hash_value, "error": f"Failed to read working_gem JSON: {e}", } # Optionally restore root-level metadata from phantom_gem # (working_gem may omit large fields such as original_ids_seen for editability) phantom_data = None try: with open(phantom_gem_path, "r", encoding="utf-8") as f: phantom_data = json.load(f) except Exception: phantom_data = None if isinstance(phantom_data, dict): for key in ( "original_ids_seen", "explicitly_preserved_ids", "export_root_id", "export_root_name", "export_root_children_status", "jewel_file", ): # Treat `None` the same as missing so JEWELMORPH can always # restore authoritative NEXUS metadata from phantom_gem. if key in phantom_data and (key not in gem_data or gem_data.get(key) is None): gem_data[key] = phantom_data[key] # Remove jewel_id fields everywhere _strip_jewel_ids(gem_data) # Build PJ-prefixed preview_tree (ephemeral, for agents/humans; never read by algorithms) nodes = gem_data.get("nodes") or [] preview_tree = [] try: preview_tree = _build_pj_preview_lines(nodes) except Exception: pass # Rebuild gem_data dict with preview_tree in early position (if dict with nodes) if isinstance(gem_data, dict) and isinstance(gem_data.get("nodes"), list): gem_data = { "export_root_id": gem_data.get("export_root_id"), "export_root_name": gem_data.get("export_root_name"), "export_timestamp": gem_data.get("export_timestamp"), "export_root_children_status": gem_data.get("export_root_children_status"), "__preview_tree__": preview_tree, "nodes": gem_data.get("nodes"), "original_ids_seen": gem_data.get("original_ids_seen"), "explicitly_preserved_ids": gem_data.get("explicitly_preserved_ids"), } # Determine phantom_jewel path (sibling to phantom_gem, fixed name) phantom_jewel_path = phantom_gem_path.with_name("phantom_jewel.json") tmp_path = phantom_jewel_path.with_suffix(".json.tmp") try: with open(tmp_path, "w", encoding="utf-8") as f: json.dump(gem_data, f, ensure_ascii=False, indent=2) os.replace(tmp_path, phantom_jewel_path) except Exception as e: return { "success": False, "hash": hash_value, "error": f"Failed to write phantom_jewel.json: {e}", } # Clean up working_gem, guardian_gem, and operations_reference.json try: if working_gem_path.exists(): working_gem_path.unlink() if guardian_gem_path.exists(): guardian_gem_path.unlink() # Delete operations_reference.json (auto-generated by JEWELSTRIKE) # Pattern: jm-[hash]-[prefix]--operations_reference.json ops_ref_pattern = working_gem_path.stem.replace("phantom_gem", "operations_reference") ops_ref_file = working_gem_path.parent / f"{ops_ref_pattern}.json" if ops_ref_file.exists(): ops_ref_file.unlink() except Exception: # Cleanup is best-effort only pass working_gem_deleted = not working_gem_path.exists() # Remove index entry for this phantom_gem try: gem_key = str(phantom_gem_path.resolve()) if gem_key in index and index[gem_key].get("hash") == hash_value: del index[gem_key] guardian_dir.mkdir(exist_ok=True) with open(index_path, "w", encoding="utf-8") as f: json.dump(index, f, indent=2, ensure_ascii=False) except Exception: # Index cleanup is best-effort only pass return { "success": True, "preview_tree": preview_tree, "hash": hash_value, "phantom_gem": str(phantom_gem_path.resolve()), "phantom_jewel": str(phantom_jewel_path.resolve()), "working_gem_deleted": working_gem_deleted, } def main() -> None: """CLI entrypoint for jewelmorph.""" args = sys.argv[1:] if len(args) != 2: print(json.dumps({ "success": False, "error": "Usage: python jewelmorph.py \"<identifier>\" \"<guardian-token>\"", })) sys.exit(1) identifier = args[0] token = args[1] result = jewelmorph(identifier, token) print(json.dumps(result, indent=2)) sys.exit(0 if result.get("success") else 1) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/daniel347x/workflowy-mcp-fixed'

If you have feedback or need assistance with the MCP directory API, please join our Discord server