From 198861f533aed9cba3093858fb63a912b1d895dc Mon Sep 17 00:00:00 2001 From: root Date: Mon, 27 Apr 2026 11:03:23 -0300 Subject: [PATCH] PPTX icon resolver: skip degraded refs from stencil buckets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symptom on the GCP→OCI PostgreSQL deck: the OCI PostgreSQL icon bbox rendered as empty space — labels and surrounding ADs all visible, but no icon where the postgres stencil should be. Root cause: ``_lookup_icon_ref`` returned the first hit from ``stencils.database_icons`` without checking it was cloneable. The bucket happens to ship one degraded entry — ``database`` is ``tag=sp`` with ``bbox=None`` (a label-strip leaf, not the real icon group) — so ``_clone_translated_block`` emitted a zero-size shape. The 19 viable refs for the same key in ``shape_library.entries`` were ignored because the early-return in ``database_icons`` won. Fix: extract a shared ``_is_viable_ref`` predicate (slide_path + child_index/node_path + non-empty bbox + supported tag) and apply it before returning from BOTH stencil buckets (``database_icons``, ``sample_icon_refs``). On miss, fall through to ``shape_library_entries`` where ``_select_preferred_ref`` already filtered the same way. Affects every alias that routes to a degraded stencil entry — ``postgresql`` / ``postgres`` / ``oci_postgresql`` were the obvious fallout because they all alias to ``database``. The Architecture Center reconstructions on shape-library-only slugs were unaffected. Verified by re-rendering examples/output-gcp-virginia-oci-postgres- adbs-clone/*.pptx — the OCI PostgreSQL bbox now shows the icon (teal stencil + "Database" intrinsic caption) consistently with the ADB-S and Refreshable Clone bboxes next to it. Co-Authored-By: Claude Opus 4.7 (1M context) --- tools/oci_pptx_diagram_gen.py | 39 +++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tools/oci_pptx_diagram_gen.py b/tools/oci_pptx_diagram_gen.py index fd34dea..c32f82d 100644 --- a/tools/oci_pptx_diagram_gen.py +++ b/tools/oci_pptx_diagram_gen.py @@ -966,24 +966,41 @@ class NativePPTXDiagramRenderer: return candidates def _lookup_icon_ref(self, key: str) -> dict | None: + # Stencil buckets sometimes ship a degraded entry — e.g. + # database_icons['database'] is tag=sp with bbox=None, which + # would clone as a zero-size shape (the "OCI PostgreSQL bbox + # but no icon" symptom Diego hit on the GCP↔OCI deck). Skip + # any ref that fails the viability check and fall through to + # the next source (sample_icon_refs, then shape_library, where + # a usable grpSp ref usually exists). database_icons = (self.index.get("stencils") or {}).get("database_icons") or {} - if key in database_icons: - return database_icons[key] - if key in self.sample_icon_refs: - return self.sample_icon_refs[key] + candidate = database_icons.get(key) + if candidate and self._is_viable_ref(candidate): + return candidate + sample = self.sample_icon_refs.get(key) + if sample and self._is_viable_ref(sample): + return sample refs = self.shape_library_entries.get(key) or [] if refs: return self._select_preferred_ref(refs) return None + @staticmethod + def _is_viable_ref(ref: dict) -> bool: + """A ref must point at a real cloneable shape (slide path, + index/path-into-shape-tree, non-empty bbox, supported tag) for + ``_clone_translated_block`` to emit visible geometry.""" + if not ref or not ref.get("slide_path"): + return False + if ref.get("child_index") is None and not ref.get("node_path"): + return False + bbox = ref.get("bbox") or {} + if not bbox.get("cx") or not bbox.get("cy"): + return False + return ref.get("tag") in {"grpSp", "pic", "sp"} + def _select_preferred_ref(self, refs: list[dict]) -> dict | None: - viable = [ - ref for ref in refs - if (ref.get("child_index") is not None or ref.get("node_path")) - and ref.get("slide_path") - and (ref.get("bbox") or {}).get("cx") - and ref.get("tag") in {"grpSp", "pic", "sp"} - ] + viable = [ref for ref in refs if self._is_viable_ref(ref)] if not viable: return refs[0] if refs else None