Root cause observed in a real Codex session: the agent burned ~3min
on `make venv` + 4 sequential `make diagram-lookup` queries trying
to find a non-existent ref-arch (OCI PostgreSQL accessed FROM GCP
via Cross-Cloud Interconnect). The catalog has 123 entries; if two
honest queries can't surface a topology, none exists — but nothing
in the skill said "stop." Three coordinated fixes:
1) Makefile: `make venv` is now idempotent.
Marker file `.venv/.deps-installed` keyed off `requirements.txt`
mtime — the second `make venv` in a row is a 26ms no-op (was
1m53s on a Codex sandbox, paid every turn). Force a rebuild with
`rm .venv/.deps-installed`.
2) kb/architecture-center/catalog.yaml: new `known_gaps` block.
Surfaced by the lookup tool as a top-of-output banner whenever
the (synonym-expanded) query tokens match any one of a gap's
`triggers` token sets. Two gaps seeded from real engagements:
- gcp-to-oci-native-services — OCI PostgreSQL/OKE/Cache/etc.
reached FROM GCP via Cross-Cloud Interconnect (the
Database@Google Cloud entries are the OPPOSITE direction).
- newer-oci-services-without-icon — OCI Cache (Redis/Valkey)
and OCI PostgreSQL post-v24.2-toolkit; no ref-arch, no
toolkit icon, fall back to generic stencil + explicit label.
Each gap's `notice:` is a copy-paste recommended composition so
the agent doesn't have to invent a strategy.
3) tools/archcenter_pattern_lookup.py: detects + surfaces gaps.
`lookup()` now returns `{"matches": ..., "gaps": ...}`. The
text printer emits a "⚠ KNOWN GAP — <id>" banner with the
notice block ABOVE the score-based top-K, so the agent sees
the stop-and-compose signal before ever scrolling to results.
YAML output mode mirrors the same shape under `gaps:`.
4) SKILL.md (option 2 step 1) + Codex copy via sync-skill.
New explicit rule: "Lookup budget: max 2 queries — never loop."
Documents the gap banner, the closest-3-then-ask fallback, and
the prohibition against grasping at a 3rd refinement.
Verified manually:
- GCP+postgresql query → gap fires
- "redis ha multi-az" → newer-OCI gap fires
- "exadata cross region data guard" → no false-positive gap
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the procedural hole that kept making Codex sessions fall back
to ``examples/`` for YAML scaffolding even after the SKILL forbade it.
The lookup tool returns a canonical Oracle ``.drawio`` as the geometry
source, but the agent had no YAML version of that geometry to copy —
the .drawio is XML with stencil-encoded icons. Without a scaffold,
agents reverse-engineered or fell back to ``examples/`` (which is
forbidden but happened anyway because the alternative was painful).
New tool ``tools/archcenter_drawio_to_template.py`` extracts:
• canvas dimensions
• containers (region / vcn / ad / subnet) classified by stroke /
fill style markers
• service bounding boxes with ``type: TODO_identify`` (the icon
type is encoded in stencil bytes — agent fills it from the
renderer's TYPE_TO_ICON aliases when authoring the spec)
• edge waypoints (source, target, points)
Output lands at ``kb/diagram/assets/archcenter-refs/<slug>/_template.yaml``
next to the .drawio. Lookup now surfaces it as
``cached: yaml=<path>`` so the agent has a one-step "copy this YAML
and adapt" path.
Ran across all 132 cached folders: 107 templates emitted, 24 had no
.drawio (zip didn't ship one), 1 already cached.
Other persistent guardrails added in this commit:
- SKILL.md option 2 step 3: explicit reminder that valid ``type:``
values come from ``TYPE_TO_ICON`` / ``ICON_TYPE_ALIASES`` in the
renderer source plus the icon-index JSON files. Do NOT grep
``examples/`` for ``type:`` — that's where the previous Codex
session was looking, against the rule.
- ``make archcenter-templates-refresh`` Makefile target.
- The lookup index now also indexes ``_template.yaml`` per slug.
- Auto-extracted templates committed under
``kb/diagram/assets/archcenter-refs/*/_template.yaml`` so a fresh
clone has them on first lookup (no extraction step needed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diego asked: does the persisted ``archcenter-refs-index.json`` auto-
update when the KB is updated? Partially — mtime on the cache root
catches add/remove of subdirs (POSIX), but NOT changes to files
INSIDE existing subdirs (description.md added, .drawio replaced).
Two fixes:
1. Both KB-update tools now ``touch`` ``kb/diagram/assets/archcenter-refs``
when they finish a run that produced changes:
- ``archcenter_description_fetcher.py`` after fetched > 0
- ``archcenter_zip_downloader.py`` after downloaded / extract /
overwrite events
The touch bumps the parent mtime, which the next lookup detects
and uses to rebuild its persisted index. Closes the silent-drift
case where description.md fetches landed but the index didn't
notice.
2. ``archcenter_pattern_lookup.py --rebuild-index`` flag — escape
hatch for manual KB edits (someone replaces a cached .drawio by
hand) where neither tool ran. Removes the stale index file before
the lookup, forcing a cold rebuild.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two persistent fixes flagged from a Codex session:
1. ``archcenter_pattern_lookup`` ran in 78 s on WSL2 because:
- ``_cached_assets`` did ``iterdir`` + recursive ``rglob`` over the
whole 113-folder cache for EVERY catalog entry (123x).
- ``_patterns_for`` reloaded ``reference-patterns.yaml`` for every
match (125 reloads = ~5 s).
- Description text from disk was read for every entry, even those
that wouldn't make the top-K.
Now: one-shot scan of the cache dir cached in-memory AND persisted
to ``kb/diagram/assets/archcenter-refs-index.json`` keyed by mtime;
patterns YAML loaded once and indexed by URL; a two-pass scoring
pipeline that only reads description text + cached_assets +
visual_patterns for the top-K candidates instead of every entry.
Result: cold run 14 s (one-time index build), warm run 1.1 s.
2. The drawio renderer had no alias for ``adb_s`` / ``adb_serverless``
/ ``autonomous_database_serverless`` / ``refreshable_clone`` —
Codex's spec used ``type: adb_s`` and the icon never resolved.
Mapped them to ``autonomous_database`` (the canonical 7-cell
stencil shipped by the OCI Toolkit), with ``adb_d`` as fallback.
Same proactive expansion in the drawio side that the PPTX side
already got (oci_goldengate, dynamic_routing_gateway, atp/adw,
kms/secret, identity/iam, iac/terraform, oac/oic, kafka).
The persisted lookup index is committed so a fresh clone hits warm
performance on first ``make diagram-lookup``.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The diagram path now follows a documented standard procedure (lookup
the closest Oracle Architecture Center reference → confirm components
→ author absolute_layout → spec validator → render → visually verify)
and ships persistent guardrails so layout regressions can't recur.
Persistent procedure changes (apply to all users, all sessions):
- tools/diagram_spec_validator.py — geometry checks (CONTAINER_TOO_THIN,
CONTAINER_PADDING_VIOLATION, LABEL_OVERFLOW_PARENT) run BEFORE either
renderer (drawio + PPTX). Catches the subnet-collapse / label-overflow
bugs that the post-render drawio validator missed.
- tools/oci_diagram_gen.py + tools/oci_pptx_diagram_gen.py — call the
spec validator before emitting any output. Adds mysql / mysql_heatwave
type aliases.
- tools/archcenter_pattern_lookup.py — scores against cached page
descriptions (not just the 1-line summary), supports --queries for
multi-fragment composition, and applies synonym expansion via
kb/architecture-center/synonyms.yaml so "LB HA cross AD" matches
"load balancer high availability availability domain".
- kb/architecture-center/synonyms.yaml — canonical synonym table
(load balancer, autonomous database, data guard, …) used by the
lookup scorer.
KB enrichment:
- tools/archcenter_description_fetcher.py + 121 cached _description.md
under kb/diagram/assets/archcenter-refs/<slug>/. Removes the runtime
dependency on docs.oracle.com when authoring specs and feeds the
pattern-lookup scorer.
- 110+ cached .drawio / .svg / .png references for offline reuse,
plus the OCI Toolkit v24.2 import (kb/diagram/assets/oci-toolkit-drawio).
Documentation:
- docs/skill/output-formats.md — new "Standard diagram-generation
procedure (MANDATORY)" + geometry rules + the new validator entry.
- SKILL.md option 2 — references the mandatory procedure.
- README.md — describes the spec validator, archcenter_pattern_lookup
and description fetcher, and updates the KB-health table.
Tooling that backs the procedure (cumulative across recent sessions):
tools/archcenter_case_runner.py, archcenter_batch_driver.py,
archcenter_zip_downloader.py, drawio_visual_validator.py,
drawio_fidelity_eval.py, harvest_drawio_icon.py, import_oci_library.py,
oci_pptx_diagram_gen.py, oci_pptx_render.py, refresh_pptx_icon_index.py.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- refresh_sku_catalog.py: add --discover to report API SKUs missing from
catalog, filtered to already-curated serviceCategory values. Auto-runs
at end of --validate and --refresh so gaps surface on every maintenance
pass. First run found 162 missing SKUs (Blackwell GPUs, X12 Ax compute,
VMware reserved tiers, WebLogic-on-OKE, Analytics/OIC BYOL).
- oci_bom_gen.py: fix Cost % column being blank. Back-fill was gated on
formula detection but data rows write raw numbers, so the predicate
never matched. Now tracks data_item_rows explicitly.
- .gitea/workflows/sku-catalog-refresh.yaml: monthly automation (1st at
09:00 UTC). Auto-refresh prices → push branch + open PR; detect new
SKUs → open issue with labels. Gated on secrets.GITEA_TOKEN.
- kb/pricing/oci-sku-catalog.yaml: add B95714 / B95715 (Autonomous ATP
Dedicated ECPU, LI + BYOL) — canonical SKUs for ADB-D pricing.
- kb/services/compute.yaml: add X12 family (VM.Standard4.Ax.Flex,
BM.Standard4.Ax.120), E6.Ax and A4.Ax variants. Verified against
docs.oracle.com computeshapes.htm. Bump last_verified + changelog.
- templates/bom-spec.yaml: document 730 hrs/month convention as default
(= real annual billing / 12; 744 overstates by 1.92% on 12-month TCO).
- Makefile: new 'make sku-discover' target.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Source: ADB-D X11M Datasheet v1.0 (Jan 2025), Table 1.
- Add HC storage variant (default): 80 TB usable disk/SS, 264 TB raw, 1.25 TB
XRMEM + 27.2 TB flash cache per SS. Keep EF variant (52.5 TB usable/SS).
- Add x11m_elastic_base (2 DB + 3 SS): 240 TB total usable disk, 192 TB Max
DB Size no-backup (what OCI Cost Estimator caps at), 96 TB with backup.
Include performance metrics (5.6M read IOPS, 300 GB/s flash bw, etc.).
- Add x11m_base_system as distinct SKU: fixed-spec, non-expandable, 192
ECPUs / 73 TB usable disk / 10 GbE — not the same as elastic 2+3.
- Replace misleading rack_configurations table (mixed X9M figures) with
elastic per-server model; 'quarter/half/full rack' noted as legacy.
- Add pricing verified against OCI Cost Estimator 2026-04-23: DB and SS at
\$2.9032/hr each; base config \$10,800/month excluding licenses.
- Add gotcha exacs_x11m_hc_vs_ef: default quotes to EF without
justification; HC serves hot data from flash cache at same latency and
far better \$/TB.
- Document the three distinct storage figures (Total Usable Disk vs Max DB
Size no-backup vs with-backup) to avoid customer confusion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- oci_deck_gen / oci_bizcase_gen: stop appending the Thank You + Oracle
logo-only closing slides at the end of every generated deck.
- oci_bizcase_gen: Business Drivers now reads primary_driver at the
drivers-level and at the top level, and renders natural-language values
verbatim (only snake_case enum tokens are Title-cased). A spec-provided
drivers.items / additional / secondary list replaces the hardcoded
"Financial Impact of Inaction" + "Operational Impact" fallback cards.
- oci-sku-catalog.yaml: add data_services category with the four real
Big Data Service SKUs (B91128/B91129/B91130/B93555) plus EST-DS-NOTEBOOK,
EST-DS-MODEL, EST-DF-SPARK as estimate placeholders (OCI Data Science /
Data Flow have no dedicated SKUs in the public pricing API — they are
billed via the underlying compute shape).
- oci_bom_gen: resolver now prints a stderr warning and tags estimate/
unknown line items with an explicit note; the xlsx writer renders
"other" category items under an "Uncategorized — confirm SKUs" section
at the end instead of silently dropping them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why this set of changes:
- KB pricing was drifting silently — domain files (database.yaml,
storage.yaml, etc.) had prices 30-800% off the live Oracle API and
nobody read them. The skill was auditing as stale on every check
with no path to fix it.
- The skill itself violated Anthropic's spec (`name` field had
uppercase/spaces) and was over the 500-line guideline (647 lines),
hurting discovery and load performance.
- Welcome flow occasionally improvised the menu instead of reading
SKILL.md, missing options.
Pricing — single source of truth, fully automated:
- Extend tools/refresh_sku_catalog.py with --refresh-domain compute,
pulls shape-level prices from the Oracle public pricing API
(apexapps.oracle.com), preserves manual fields (notes, GPU specs,
free-tier annotations, estimation_helpers), recomputes derived
monthly values, and protects $0 free-tier prices from overwrite.
- Delete 12 redundant pricing/<domain>.yaml files. They duplicated
oci-sku-catalog.yaml with worse abstractions and were nobody's
source of truth (no tool consumed them).
- Migrate the genuinely valuable knowledge from those 12 files
(billing models, BYOL rules, free-tier rules, ECPU vs OCPU,
X11M elastic model, hyperscaler comparisons, service nuances)
into kb/field-knowledge/pricing-knowledge.yaml — non-numeric,
no refresh needed.
- Result: pricing freshness check goes from 13 stale files to 0.
KB freshness automation:
- Add tools/kb_freshness.py — wrapper around kb_linter.check_freshness()
with --check, --auto-refresh, --json, --quiet modes. Bridges stale
files to their refresh tools (SKU catalog, compute domain, arch
center). Wired into the welcome flow as a pre-flight banner that
asks the user before refreshing.
- Fix pre-existing kb_linter bug: it crashed on the 45 multi-doc
YAML files (frontmatter + body pattern) because it used safe_load
instead of safe_load_all. Freshness check was effectively dead.
- Standardize timestamp field: linter now accepts last_verified,
last_updated, and last_refreshed; refresh_arch_catalog writes
last_verified instead of last_refreshed.
- Add make freshness / make freshness-refresh targets.
Skill alignment with Anthropic Agent Skills best practices:
- Rename `name: OCI Deal Accelerator` to `oci-deal-accelerator`
to comply with the [a-z0-9-]{1,64} spec.
- Refactor SKILL.md from 674 to 445 lines via progressive disclosure:
extract WA review output format, ECAL readiness format, and output
conventions into docs/skill/*.md referenced from the main file.
- Add scripts/sync-skill.py + make sync-skill: source of truth is
root SKILL.md, .agents/skills/oci-deal-accelerator/SKILL.md is
auto-generated. make lint validates sync.
- Add evaluations/ with 3 manual baseline scenarios (welcome-flow,
full-proposal, wa-review) per the Anthropic best-practices guidance
to "build evaluations first."
Welcome flow hardening:
- Tighten CLAUDE.md to MANDATE reading SKILL.md before showing the
menu (no improvising), and document the freshness pre-flight check
with the ask-before-refresh user flow.
- Update SKILL.md welcome flow to instruct: parse kb_freshness JSON,
show banner with stale count + oldest file, prompt user to refresh
(only when an automated tool exists), fall back silently on errors.
Linter hygiene (zero remaining issues):
- Expand config/kb-tags.yaml taxonomy with features, operations,
metrics, limitations sections covering 31 previously-unknown tags
used in field findings (rac, ecpu, refreshable-clone, hnsw, etc.).
- Assign owners for kb/compatibility/, kb/competitive/,
kb/well-architected/ (Diego Cabrera as default until team grows);
kb/pricing/ marked as "Auto-refreshed" since it no longer needs
human ownership.
- kb_linter accepts top-level `date` as fallback for contributor
block; migrate FF-202603-008 from legacy `reported_by` to
contributor block.
- Result: linter goes from 45 issues to 0.
Other:
- Recompute estimation_helpers monthly values in compute.yaml after
the price refresh (they were derived from the old E5/A1 numbers).
- Add kb/README.md — contributor guide (directory map, frontmatter
spec, refresh tooling, review cadence).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New tools/oci_bom_gen.py: generates .xlsx BOM from YAML spec with
Oracle Redwood styling, Excel formulas, and Oracle disclaimer.
Supports standard BOM (--output) and AppCA import format (--appca)
- New tools/refresh_sku_catalog.py: refreshes SKU catalog prices from
Oracle's public pricing API (apexapps.oracle.com)
- New kb/pricing/oci-sku-catalog.yaml: ~160 OCI SKUs across 14
categories, validated against Oracle API
- New templates/bom-spec.yaml: input spec template for BOM generation
- Add SA TOOLS section to SKILL.md (options 13-14)
- Register BOM generator in oci_output.py