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>
Diego flagged two patterns from the Codex session:
1. Aliases for natural-language slugs were missing — Codex authored
``type: refreshable_clone`` and ``type: oci_goldengate`` and the
resolver returned None for both. Beyond fixing those two, expand
the table proactively: 50+ slugs an SA / agent might naturally
reach for (atp, alb, nlb, apigw, lambda, kms, secret, kafka,
alarm, email, identity, iam, iac, terraform, oac, oic, language /
speech / vision, rpc / lpg / cpe, health_check, tags, etc.) all
mapped to canonical icon entries that exist in
kb/diagram/oci-pptx-icons-index.json.
2. Codex was editing tools/oci_deck_gen.py and the deck-spec while
fulfilling a menu-driven option-2 request — going off the
procedure to "fix" perceived bugs in the renderer. New rule in
SKILL.md § What You Do NOT Do: when the user is in menu mode
(options 1–14), the agent USES the existing tools, never modifies
them. Tool gaps (missing aliases, broken paths) get surfaced as
end-of-reply follow-ups for a separate developer-mode session.
Two narrow exceptions kept: kb/field-findings/tracker.yaml via
option 11, and writing customer spec/output YAML under examples/.
Codex copy auto-synced via the pre-commit hook.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diego flagged that Codex, when authoring an absolute_layout, fell
back to grepping ``examples/`` for AWS/OCI multicloud patterns even
though the procedure said not to. The reason was real: the lookup
tool returns a cached ``.drawio`` (canonical Oracle geometry) but
no canonical ``absolute_layout.yaml`` template, so the agent had no
YAML scaffold to copy the spec shape from.
Tighten the rule rather than ban it outright:
• Cached ``.drawio`` = GEOMETRY source. Container nesting,
padding, AD/subnet placement, icon choices come from there.
• ``examples/`` = YAML scaffold reference ONLY (which fields
exist, how connections are authored, fontSize unit). Do NOT
copy geometry numbers — those came from a different topology.
Rule of thumb baked into the SKILL: if you're copying (x, y, w, h)
from examples/, stop and copy from the cached .drawio instead.
(Codex copy auto-synced via the pre-commit hook installed earlier.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diego asked the right question: what if someone edits the Codex
side directly? Until now the sync was unidirectional (root →
.agents/) but the hook only fired when root SKILL.md was staged —
isolated .agents/ edits sneaked past the hook locally and only got
caught by CI's --check. A future ``make sync-skill`` would then
silently destroy them.
Hardened both layers to reject the asymmetric case at the moment
of the commit/PR:
- Pre-commit hook now classifies the staged set and REJECTS
commits that stage .agents/skills/.../SKILL.md in isolation,
with a step-by-step message: move the edit into root SKILL.md
and re-stage. The hook still auto-syncs when root SKILL.md is
staged.
- CI gate adds a second step (PR-only) that diffs the PR's base
against head; if .agents/ moved but root SKILL.md didn't, fails
with an actionable error before the build proceeds.
- README documents the direction explicitly: edits land in root
SKILL.md; .agents/ is auto-generated; Codex-specific instructions
belong in AGENTS.md, not in the SKILL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-layer defense to keep root SKILL.md (Claude Code) and
.agents/skills/oci-deal-accelerator/SKILL.md (Codex) byte-aligned.
Without this, Claude Code and Codex can drift and give the user
contradictory instructions — exactly the failure mode that produced
the workload-driven-mode incident on 2026-04-25.
Layers (least → most enforcement):
1. Local pre-commit hook (.githooks/pre-commit, opt-in via
``make install-hooks`` which sets core.hooksPath). When SKILL.md
is staged, auto-runs scripts/sync-skill.py and stages the
regenerated .agents/ copy in the same commit. Idempotent.
2. ``make install-hooks`` target + a tip line in ``make venv``'s
output so any new clone discovers the hook setup.
3. CI gate (.gitea/workflows/skill-sync.yaml) on every push/PR
touching SKILL.md or the .agents/ copy. Runs
``scripts/sync-skill.py --check`` and fails the build on drift.
Catches anyone who skipped the hook.
README documents the rationale and the three layers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Codex session showed why the workload-driven mode is the wrong
default. Codex authored a spec under
``tenancy → regions → compartments → services`` (the documented
"workload-driven" mode), generated two .drawio files, and reported
success. The output was unusable: services rendered as colored
rectangles with text instead of OCI icon stencils, connector text
went into the edge value (forbidden by the connector-label rule),
and the spec validator never ran because it only fires on
``absolute_layout`` blocks.
Diego: "los drawio son horribles, por que sucede eso?".
Three persistent fixes:
1. SKILL.md option 2 now says ``absolute_layout`` is REQUIRED for
step 3, calls out workload-driven explicitly as forbidden, and
explains *why* (no stencils, no validator).
2. docs/skill/output-formats.md § Diagram modes is rewritten — mode
1 is ``absolute_layout`` (REQUIRED), mode 2 is workload-driven
(DEPRECATED, back-compat only). Removes the "two acceptable
shapes" framing that let Codex pick the wrong one.
3. ``oci_diagram_gen.py`` now prints a loud stderr WARNING when a
spec takes the workload-driven path, naming the procedure docs.
Catches any agent that skipped reading SKILL.md before generating.
Codex copy synced via ``make sync-skill``.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A Codex session showed the gap: option 2's prose asked the user to
describe the architecture but only mentioned the output formats in
passing ("if you want X / if you want Y"), so the agent never asked
which format(s). Codex also skipped the reference-architecture lookup
and went straight to grepping ``examples/`` for pre-existing specs —
``examples/`` is previous user output, not the authoritative Oracle
catalog.
Two persistent fixes for any agent (Claude Code, Codex, Cursor, …):
1. Option 2 now asks two explicit questions in one message and waits
for both answers before doing anything: (1) describe / paste spec,
(2) pick output format(s) — drawio, native PPTX, or both.
2. The procedure is restated as a numbered list directly under
option 2 (no longer a single-sentence reference to
docs/skill/output-formats.md). Step 1 explicitly says use
``archcenter_pattern_lookup.py`` against the catalog and explicitly
forbids using ``examples/`` as a reference source.
Synced to the Codex copy at .agents/skills/oci-deal-accelerator/SKILL.md
via ``make sync-skill`` so both agents see the same instructions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs flagged on the MySQL HeatWave HA example (2026-04-25 round 5):
1. ``render_standard_sections: false`` under the ``output:`` block was
silently ignored — the generator only checked the top-level key.
Result: every spec that put the flag where the docs suggest got the
full standard deck PLUS the custom slide, producing a duplicated
"Architecture Overview" + dedicated diagram slide. Now the
generator honors both locations.
2. Architecture Principles slide allocated only 0.4" per bullet —
exactly one 11pt line. Wrapping principle summaries (e.g. D-03
Use-Case Fit's 125-char text) overflowed into the next bullet's
slot, visually overlapping D-02. Diego: "D-03 esta superpuesto a
D-02, lo mismo pasa en otras columnas". Bumped per-item height to
0.95" — fits 2-3 wrapped lines with breathing room.
3. PPTX rasterizer (oci_pptx_render.py) gained word-wrap so the
local PNG preview matches what real PowerPoint draws. Without it,
long text rendered on a single line off the column and the
principles fix looked broken in preview when it was actually fine
in PowerPoint.
Persistent: all three fixes apply to every deck the skill generates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the gap between what the validator catches and what Diego
flagged on the MySQL HeatWave HA example (2026-04-25 round 4):
- CONTAINER_PADDING_VIOLATION now checks all four edges (top / bottom /
left / right). Previously only bottom — DB Subnet's left/right edges
touching the AD borders went unflagged.
- CONNECTOR_OVER_LABEL now runs at SPEC level (was post-render drawio
only). Catches "Network Load Balancer label sits below the arrows so
the arrows cross it" before either renderer emits anything; covers
the PPTX path too.
Implemented via Liang-Barsky line-rect clipping on every connector
segment vs every free-floating label bbox.
Renderer policies persist for every new diagram (bent connectors,
icon-group preference, mysql aliases). Spec-level rules are now
enforced earlier and on both render paths, so a spec authored against
the procedure will pass before any artifact is produced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec authors hand the renderer a polyline like
``points: [[210,280],[400,280],[700,130]]``. The previous code emitted
one ``prst="line"`` shape per segment, so the elbow was two separate
straight lines that PowerPoint cannot recognise as a connected path.
The OCI template (kb/diagram/assets/OCI_Icons.pptx) uses native
PowerPoint bent connectors instead — 98 ``straightConnector1`` plus
22 ``bentConnector2/3/5`` plus 15 ``line`` across 48 slides. Match
that convention:
• 2 points → straightConnector1 (one segment, unchanged)
• 3 points → bentConnector2 (one elbow)
• 4 points → bentConnector3 (S-shape, two elbows)
• 5+ points → bentConnector5 (four elbows)
The rasterizer (oci_pptx_render.py) now also understands bentConnectorN
so the local PNG preview matches what real PowerPoint draws — without
this it rendered every connector as one straight diagonal regardless of
the prst.
Persistent: applies to every absolute_layout connector for every spec,
not a per-case patch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OCI_Icons.pptx ships both a full icon group (tag=grpSp, ~0.7" tall)
and a tiny "label strip" (tag=sp, ~0.2" tall) under the same slug for
many services. The previous resolver picked whichever came first in
the index, which for 'mysql' returned the text strip — so the PPTX
diagram showed a label-only shape instead of the dolphin/HeatWave
icon, while drawio rendered the proper stencil.
Two fixes, both persistent (apply to every icon family, not just MySQL):
- TYPE_TO_ICON for mysql/heatwave now lists 'mysql_heatwave' (the
grpSp) before 'mysql' (the strip).
- _select_preferred_ref now filters viable refs to those that are
either grpSp/pic OR have a bbox ≥0.4"x0.4" — text strips fall out
before the scoring function runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements points 3–8 of the post-procedure follow-up.
- Make targets: diagram-lookup, diagram-validate-spec, diagram-spec-audit,
archcenter-descriptions-refresh, archcenter-smoke. Promotes the new
tools out of "remember the CLI" into discoverable build-automation.
- .gitea/workflows/diagram-validators.yaml — runs spec validator across
every absolute_layout spec on push/PR, plus a drawio re-render smoke
test on the canonical example. Hard fail blocks merges.
- archcenter_pattern_lookup --llm-rewrite — opt-in (ANTHROPIC_API_KEY
env var, mirroring DRAWIO_EXE pattern). Rewrites natural-language
queries into canonical OCI terminology before scoring; falls back to
the original query on any error.
- diagram_spec_validator — documents the policy on legacy archcenter
reproductions: LABEL_NEAR_PARENT_EDGE stays a warning so verbatim
reconstructions don't lose pixel-fidelity from cosmetic edits.
- README — documents the asset refresh procedure (downloader +
description fetcher are idempotent, refresh quarterly).
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>
Previous curl calls ignored exit codes — PR/issue creation was silently
failing (branch got pushed but no PR/issue landed) while the step reported
green. Now each curl captures the response body and HTTP status, prints
both for debugging, and exits non-zero if status != 201.
Also dropped the labels field from the issue payload — Gitea rejects
issue creation when referenced labels don't exist in the repo, which
may have contributed to the silent failure. Labels can be added
manually post-creation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prior run pushed automation/sku-refresh-2026-04-24 successfully but
failed before opening the PR (issue-creation bug, now fixed). On the
next run, main has moved, so a new commit on the same branch name has
a different parent → non-fast-forward rejection.
Append ${{ github.run_number }} to make the branch unique per workflow
invocation. Orphan branches left behind can be cleaned up after their
PRs are merged/closed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The workflow's grep-on-free-text approach was fragile: local run correctly
extracted 162, but in the CI run the 'Open issue' step was skipped, meaning
new_count ended up as 0 after parsing. Likely causes: stdout buffering,
color codes, or shell differences between local and the Gitea runner's
shell.
Fix: tools/refresh_sku_catalog.py --discover now emits a deterministic
last-line marker `DISCOVER_MISSING_COUNT=<n>`. The workflow parses that
with sed (anchored, unambiguous) instead of the human-facing summary
line. Also added a diagnostic echo so the parsed value shows up in CI
logs for future debugging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gitea Actions runner does not support actions/upload-artifact v2.0.0+
(error: "@actions/artifact v2.0.0+, upload-artifact@v4+ and
download-artifact@v4+ are not currently supported on GHES").
The v4 release switched to a new artifact API that only GitHub-hosted
runners implement. v3 still uses the v1 API and works on Gitea /
self-hosted / GHES runners.
Downgraded in both sku-catalog-refresh.yaml and kb-health.yaml.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The oci-mcp-runner uses node:20-bookworm which runs as root and does
NOT ship with sudo installed. Our workflows used 'sudo apt-get' → step
failed with 'sudo: command not found' (exit 127) on first invocation
of sku-catalog-refresh.yaml.
Fix both affected workflows (sku-catalog-refresh + kb-health):
- Drop the `sudo` prefix (we're already root).
- Install Python deps via apt packages (python3-requests, python3-yaml,
python3-bs4) instead of pip. Avoids PEP 668 / externally-managed
environment error on Debian 12 and is faster/cacheable by the runner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch from manually-configured secrets.GITEA_TOKEN to the auto-provisioned
secrets.GITHUB_TOKEN that Gitea Actions creates per run. Scoped to the repo,
rotates per workflow run, no manual config needed.
Added explicit permissions block (contents:write, pull-requests:write,
issues:write) so the ephemeral token has the scopes required to push a
branch, open the PR, and open the issue for new SKUs.
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>
Service Tiering slide no longer falls back to the workload `name`
field for the tier label, which previously rendered "Bronze Bronze"
when the spec only carried tier names. Uptime/RTO/RPO now fill from
tier defaults when the spec omits them.
Architecture Principles slide enriches caller-supplied items with
name + summary from the KB when only the principle id is given,
instead of rendering bare placeholders like "P-01 P-02".
Architecture Overview slide auto-builds a two-region visual when the
proposal spec names a `dr_region` but doesn't pre-render `architecture.visual`,
so DR is no longer dropped from the deck.
Diagram generator gained a region-level `local_dr` flag that adds a
"Local DR Standby" dormant node inside the region, and now auto-wires
an "RPC (Remote Peering)" edge between DRGs when the spec defines
two or more regions with DRGs but no manual peering connection.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Architecture Overview slide was rendering the DR region with a fallback
label (e.g. "DR STANDBY" with no region name) because _adapt_flat_spec()
only consulted disaster_recovery / architecture.dr — it never looked at
the dr_region / tenancy.regions fields the MCP actually emits. The
adapter now discovers the primary and DR regions from:
- spec.primary_region / spec.region / architecture.primary_region
- spec.dr_region / spec.secondary_region / architecture.dr_region
- tenancy.regions[] entries (strings or {region, role} dicts, with role
in {primary, hub, prod, production} vs {dr, standby, secondary,
failover})
When both regions are known, the adapter emits an architecture.visual
with two labeled region blocks so the DR region renders with its real
name (e.g. "mx-monterrey-1 [DR STANDBY]") instead of the placeholder.
If the spec already supplies architecture.visual or diagram_path, those
are preserved instead of overwritten.
Operational RACI slide kept being skipped for payloads that passed
operational_raci in a non-canonical shape. The from_spec handler now
accepts:
- Top-level key aliases: operational_raci, raci, operations_raci
- Bare string ("fully_managed") → loads KB defaults for that model
- List of dicts → used directly as raci_items
- Dict with raci_items / items / activities and model / engagement_model
- Empty raci_items → falls back to KB defaults by model
The flat adapter also now respects a user-supplied operational_raci
instead of overwriting it with the co_managed default. Verified:
operational_raci: "fully_managed" renders the Fully-Managed RACI table
with 18 activities; bare flat MCP payload still gets the 13-activity
co_managed default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regression after the closing-slide removal surfaced a separate bug:
_adapt_flat_spec() only emitted metadata + summary + cost, so decks
generated from the MCP flat payload collapsed to 3-5 slides of title +
summary. The deck generator's ECAL sections (service_tiering,
architecture_principles, environment_catalogue, operational_raci, etc.)
all check top-level spec keys the adapter never produced.
Adapter now also emits, from the flat payload + kb/patterns defaults:
- service_tiering from services[] (preserves any tier/uptime/rto/rpo)
- architecture / architecture_principles (ECAL "always" picks)
- ha_dr tiers derived from the distinct service tiers present
- security baseline (IAM/network/database/monitoring controls)
- environment_catalogue (Prod/Pre-Prod/Dev-Test, +DR if enabled)
- operational_raci co_managed default from the KB
- next_steps skeleton
Flat MCP payload now renders 11 content slides instead of 3-5, and the
proposal-spec.yaml path (non-flat) is unchanged — still 16 slides for
examples/proposal-spec.yaml.
Also documents the new data_services SKUs in docs/bom-cookbook.md as
Recipe 4 (BDS/DS/DF) so MCP payloads can use the catalog codes directly.
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>
Harden bizcase, bom, deck, and diagram generators to tolerate payload
shape variants (metadata/cover/summary/line_item aliases, current_state
as string, alternate pillar keys) so MCP and CLI flat specs render
consistently. Add input-alias tests per generator.
Also loosen KB governance tests to handle multi-document service YAMLs
with optional changelogs, untrack the customer demo output under
examples/output-demo-pharma-mx/ (matches .gitignore), and ship an
ADB-S vs Aurora 500GB sample deck.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex sessions burn 10+ tool calls grepping kb/field-knowledge and the SKU
catalog to map standard requirements (ExaCS X11M BYOL, ADB-D, ADB-S + block
+ FastConnect) to the 4-5 SKUs the MCP BOM tools need. Bake the recipes
into docs/bom-cookbook.md so the LLM can skip the exploration loop for
well-known patterns, and link it from SKILL.md between Output Generation
and Knowledge Base (same progressive-disclosure pattern the other docs use).
Recipe 2 (ADB-Dedicated) explicitly calls out the infra-SKU overlap with
ExaCS — one of the specific traps the exploration loop keeps hitting.
Catalog-fallback note keeps the cookbook honest when a requirement does
not match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
generate_deck on the MCP server passes a flat spec (customer_name, title,
workload_type, services, cost_summary, ...) — but from_spec only understood
the proposal-spec YAML shape (metadata/summary/architecture/...). With no
matching keys, only the title and closing layouts rendered, both populated
with empty strings, producing a deck that looked blank.
Detect the flat shape in from_spec and adapt it into the proposal-spec
fields that the existing slide builders already consume. Title, summary
(target state + current-state bullets) and cost slides now render from
whatever the MCP tool sends; the richer proposal-spec path is untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
actions/setup-python@v5 fails on the arm64 Gitea runner with "version '3.12'
with architecture 'arm64' was not found" — the prebuilt Python manifest
doesn't cover this runner/arch combo. Use the system python3 from apt; the
link-check scripts don't require 3.12 specifically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 13MB template was caught by the global *.pptx ignore rule, leaving
generate_deck failing with "Template not found" in any environment that
builds from a fresh clone (MCP server container included). Whitelist it
alongside the existing sample-output exception.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex users hit `python: command not found` on the welcome-flow
pre-flight because SKILL.md called `python tools/kb_freshness.py`
directly. Any hardcoded version (python/python3/python3.12) breaks
for somebody — users have 3.8, 3.10, 3.11, 3.12 in the wild.
Fix: channel all skill-initiated Python calls through `make` targets
so the Makefile's single $(PYTHON) variable (auto-detected venv >
3.12 > 3.11 > 3.10 > python3) is the only place Python resolution
lives.
- Makefile: add `kb-check` target emitting JSON for the skill pre-flight
- SKILL.md + .agents/skills/.../SKILL.md: call `make kb-check` and
`make freshness-refresh` instead of bare `python tools/...`
- CLAUDE.md: same fix in the Welcome Flow instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Makes the skill work on any laptop regardless of installed Python version
or LLM harness, without per-command approval prompts or missing deps.
- Makefile: auto-detect Python (venv > 3.12 > 3.11 > 3.10 > python3)
and new `make venv` target that picks the best Python at creation time
- .claude/settings.json: project-level Claude Code permissions
(Write to examples/ and output/, common bash commands pre-authorized)
- .codex/config.toml: Codex sandbox config with network_access=true,
approval_policy=never, sandbox_mode=workspace-write — fixes
`make venv` failing with "No matching distribution" in Codex
- CLAUDE.md / AGENTS.md: document the `make venv` flow, drop all
hardcoded python3.12 references in favor of make targets
- CLAUDE.md: add Karpathy-style coding guidelines (think before coding,
simplicity first, surgical changes, goal-driven execution)
- .gitignore: add .venv/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 improvements based on Oracle Architecture Center reference diagrams:
1. Edge label offset — labels pushed 15px away from edge midpoint via
mxGeometry offset injection, prevents overlap with icons
2. Auto-sizing containers — regions, VCNs, tenancy calculate dimensions
from content instead of using fixed sizes (DR region: 540→260px)
3. jettySize=auto on all edges — clean stubs leaving/entering shapes
4. Dual connection merging — duplicate from/to pairs merged into single
edge with combined label (e.g., dual FastConnect)
5. DRG placed OUTSIDE VCN, INSIDE region — matches Oracle ref arch
pattern where DRG is the central hub between external and VCN
6. container=1;collapsible=0 injected into container XML styles
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Syncs skill files via rsync over SSH, restarts MCP containers, and waits
for healthy status. Requires OCI_SSH_KEY and OCI_HOST secrets in Gitea.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes two issues: (1) diagrams adding unrequested components (EBS, WAF, Vault,
Monitoring, etc.) by replacing vague "implied dependencies" with a closed
whitelist of 6 technical dependencies and making pre-generation review mandatory
with 3 sections (REQUESTED/TECHNICAL/OPTIONAL). (2) Unstructured data consumption
by adding mandatory Extraction Receipt (CONFIRMED/INFERRED/MISSING), source
tracking in workload profiles, and completeness gate per engagement tier.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: elbow=vertical conflicted with orthogonalEdgeStyle + port
constraints, causing arrows to route backwards and cross content.
Changes to oci_diagram_gen.py (generic, all diagrams):
- Remove elbow=vertical from edge base style
- Use edgeStyle=orthogonalEdgeStyle explicitly in extra_style
- Add jumpStyle=arc;jumpSize=8 — crossing edges show arc, not overlap
- Remove drawpyo waypoints="orthogonal" (now controlled via extra_style
to avoid style merging conflicts)
- Vertical edges with labels: align=left so text doesn't overlap icons
- Regression tested with PharmaCorp (dual-region) and MELI (single-region)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Edges now auto-detect exit/entry ports based on relative positions of
source and target services. Uses absolute coordinates from the layout
engine to determine whether connections should exit right→left,
top→bottom, etc.
- exitX/exitY/entryX/entryY added to every edge style
- 1.5x threshold favors vertical routing over horizontal when positions
are diagonal — matches Oracle ref arch top-down subnet flow
- Eliminates crossing arrows in multi-subnet, multi-region diagrams
- Verified with both PharmaCorp (dual-region) and MELI (single-region)
Generic code fix — applies to all diagrams, not just this spec.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
oci_diagram_gen.py auto-layout improvements:
- Gateways now placed FIRST, vertically centered on VCN left edge
- Subnets offset RIGHT by gateway lane width to avoid overlap
- Gateway lane width auto-calculated from widest gateway + padding
- Network edges: teal color, thicker (2px) for visual hierarchy
- Internal edges: gray dashed for management connections
- Data edges: solid charcoal (unchanged, default)
These are generic code fixes — apply to any diagram spec, not just
the PharmaCorp demo. Verified with both PharmaCorp and MELI specs.
Also regenerated all PharmaCorp deliverables with prior fixes:
- Business case: PharmaCorp (was MELI), correct TCO/ROI/value schema
- BOM: verified SKUs from catalog, 45% global discount applied
- PDF: environment catalogue with new schema
- Diagram: clean layout with gateway offset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
oci_bom_gen.py from_spec now reads metadata.discount_pct as fallback
when individual line items don't specify a discount. This means
discount_pct: 0.45 in the spec metadata applies 45% to every SKU.
Previously, global discount was silently ignored — only per-item
discount fields were read (defaulting to 0.0).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>