diff --git a/examples/output-cli-improvements-deliverables/arch-search-adb-hadr.json b/examples/output-cli-improvements-deliverables/arch-search-adb-hadr.json
new file mode 100644
index 0000000..017dfe2
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/arch-search-adb-hadr.json
@@ -0,0 +1,378 @@
+[
+ {
+ "title": "Deploy Oracle Autonomous Database on Oracle Database@Azure",
+ "url": "https://docs.oracle.com/en/solutions/deploy-autonomous-database-db-at-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2024-10",
+ "services": [
+ "adb-s",
+ "adg",
+ "azure"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr",
+ "autonomous"
+ ],
+ "summary": "Multi-AZ deployment of ADB-S on Database@Azure with Autonomous Data Guard. Recommends VNet peering between app and DB VNets, TAC for availability, and ADG standby in a different AZ for automatic failover.\n",
+ "terraform": null
+ },
+ {
+ "title": "Oracle MAA for Oracle Database@Azure",
+ "url": "https://docs.oracle.com/en/solutions/oracle-maa-db-at-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-05",
+ "services": [
+ "exacs",
+ "adg",
+ "vault",
+ "azure"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr",
+ "autonomous"
+ ],
+ "summary": "Cross-AZ Data Guard on ExaCS in Database@Azure. Active Data Guard recommended for cross-AZ replication (block repair, app continuity, read offload). Backups to Autonomous Recovery Service.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement disaster recovery with local and regional standbys on Oracle Database@Azure",
+ "url": "https://docs.oracle.com/en/solutions/local-regional-standby-dr-db-at-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-05",
+ "services": [
+ "exacs",
+ "adg",
+ "azure",
+ "fsdr"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr"
+ ],
+ "summary": "Local standby for HA within same region, regional standby for DR across Azure regions. Uses Active Data Guard with Fast-Start Failover. Recommends Full Stack DR service for automated failover orchestration.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement disaster recovery with multi-region standby on Oracle Database@Azure",
+ "url": "https://docs.oracle.com/en/solutions/multi-region-standby-dr-db-at-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-04",
+ "services": [
+ "exacs",
+ "adg",
+ "azure"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr"
+ ],
+ "summary": "Multi-region DR for ExaCS on Database@Azure using Data Guard. Cross-region standby with switchover/failover capabilities. Includes network topology for Azure-to-Azure cross-region connectivity.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement cross-region disaster recovery for Exadata Database on Oracle Database@Azure",
+ "url": "https://docs.oracle.com/en/solutions/exadb-dr-on-db-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-01",
+ "services": [
+ "exacs",
+ "adg",
+ "azure",
+ "fsdr"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr"
+ ],
+ "summary": "Cross-region DR for ExaCS on Database@Azure. OCI-managed networks for peering (better performance, first 10 TB/month free). Data Guard for cross-region replication, FSDR for automated failover orchestration.\n",
+ "terraform": "https://github.com/oracle-quickstart/oci-multicloud-azure"
+ },
+ {
+ "title": "Deploy Active Data Guard Far Sync to protect data across Oracle Database@Azure regions",
+ "url": "https://docs.oracle.com/en/solutions/active-data-guard-far-sync-oracle-db-at-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-02",
+ "services": [
+ "exacs",
+ "adg",
+ "azure"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr"
+ ],
+ "summary": "Far Sync instance for zero data loss protection across Azure regions. Synchronous redo transport to Far Sync, async to remote standby. Eliminates performance impact of synchronous shipping over long distances.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement Oracle Database Autonomous Recovery Service with Oracle Database@Azure",
+ "url": "https://docs.oracle.com/en/solutions/implement-recovery-service-db-at-azure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-01",
+ "services": [
+ "exacs",
+ "azure"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "azure",
+ "ha-dr"
+ ],
+ "summary": "Autonomous Recovery Service for ExaCS backups on Database@Azure. Automated backup management with real-time protection and point-in-time recovery capabilities.\n",
+ "terraform": null
+ },
+ {
+ "title": "Deploy Oracle Autonomous Database on Oracle Database@Google Cloud",
+ "url": "https://docs.oracle.com/en/solutions/deploy-adb-db-at-google-cloud/index.html",
+ "type": "reference-architecture",
+ "date": "2025-03",
+ "services": [
+ "adb-s",
+ "adg",
+ "google-cloud"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "ha-dr",
+ "autonomous"
+ ],
+ "summary": "ADB-S on Database@Google Cloud with cross-region ADG. Non-overlapping CIDR between VPC and VCN required. App tier should span 2+ AZs with Google Global Load Balancer for failover.\n",
+ "terraform": null
+ },
+ {
+ "title": "Deploy Active Data Guard Far Sync to protect data across Oracle Database@AWS regions",
+ "url": "https://docs.oracle.com/en/solutions/dr-active-dg-farsync-db-at-aws/index.html",
+ "type": "reference-architecture",
+ "date": "2025-09",
+ "services": [
+ "exacs",
+ "adg",
+ "aws"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "aws",
+ "ha-dr"
+ ],
+ "summary": "Far Sync instance for zero data loss protection across AWS regions. Synchronous redo to local Far Sync, asynchronous to remote standby. Requires cross-region AWS networking for redo transport.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement disaster recovery with local and regional standbys on Oracle Database@AWS",
+ "url": "https://docs.oracle.com/en/solutions/dr-local-regional-standby-db-at-aws/index.html",
+ "type": "reference-architecture",
+ "date": "2025-09",
+ "services": [
+ "exacs",
+ "adg",
+ "aws",
+ "fsdr"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "aws",
+ "ha-dr"
+ ],
+ "summary": "Local standby for HA within same AWS region, regional standby for DR. Active Data Guard with Fast-Start Failover for automated failover. Full Stack DR service for cross-service failover orchestration.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement disaster recovery with cross-zonal Data Guard on Oracle Database@AWS",
+ "url": "https://docs.oracle.com/en/solutions/cross-zone-dr-db-at-aws/index.html",
+ "type": "reference-architecture",
+ "date": "2025-08",
+ "services": [
+ "exacs",
+ "adg",
+ "aws"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "aws",
+ "ha-dr"
+ ],
+ "summary": "Cross-AZ Data Guard on Database@AWS for HA. Synchronous redo transport between AZs for zero data loss. Fast-Start Failover for automatic database switchover on failure.\n",
+ "terraform": null
+ },
+ {
+ "title": "Implement disaster recovery with cross-regional Data Guard on Oracle Database@AWS",
+ "url": "https://docs.oracle.com/en/solutions/implement-dr-cross-region-dg-odb-aws/index.html",
+ "type": "reference-architecture",
+ "date": "2025-08",
+ "services": [
+ "exacs",
+ "adg",
+ "aws"
+ ],
+ "tags": [
+ "database",
+ "multicloud",
+ "aws",
+ "ha-dr"
+ ],
+ "summary": "Cross-region Data Guard on Database@AWS for DR. Asynchronous redo transport for cross-region replication. Planned switchover for maintenance, forced failover for disaster scenarios.\n",
+ "terraform": null
+ },
+ {
+ "title": "Migrate Oracle RAC Databases to OCI",
+ "url": "https://docs.oracle.com/en/solutions/ensure-ha-migrate-vmware-workloads-to-oci/migrate-oracle-rac-databases1.html",
+ "type": "reference-architecture",
+ "date": "2025-01",
+ "services": [
+ "exacs",
+ "exascale",
+ "adb-d",
+ "base-db"
+ ],
+ "tags": [
+ "database",
+ "migration",
+ "ha-dr"
+ ],
+ "summary": "Compares 4 RAC migration targets: ExaCS Dedicated, Exascale, ADB-D, and 2-node RAC on Base DB. ExaCS recommended for full RAC feature parity. Exascale for 23ai-only without dedicated infra commitment.\n",
+ "terraform": null
+ },
+ {
+ "title": "Deploy ORDS with High Availability on OCI",
+ "url": "https://docs.oracle.com/en/solutions/deploy-ords-ha-oci/index.html",
+ "type": "reference-architecture",
+ "date": "2025-06",
+ "services": [
+ "compute",
+ "load-balancer",
+ "adb-s",
+ "bastion"
+ ],
+ "tags": [
+ "database",
+ "application",
+ "ha-dr"
+ ],
+ "summary": "Multi-instance ORDS behind OCI Load Balancer for HA REST access to Oracle DB. Works with ADB-S, DBCS, or ExaCS. Recommends DB in private subnet, ORDS in public with granular NSG rules.\n",
+ "terraform": null
+ },
+ {
+ "title": "Configure Data Guard for Oracle Exadata Database Service on Dedicated Infrastructure",
+ "url": "https://docs.oracle.com/en/solutions/dataguard-exadata-dedicated-infrastructure/index.html",
+ "type": "reference-architecture",
+ "date": "2025-07",
+ "services": [
+ "exacs",
+ "adg"
+ ],
+ "tags": [
+ "database",
+ "ha-dr"
+ ],
+ "summary": "Data Guard configuration for ExaCS Dedicated. Active Data Guard for read offload and automatic failover. Cross-region standby for DR with Fast-Start Failover for automated switchover.\n",
+ "terraform": null
+ },
+ {
+ "title": "Back up from Exadata on Dedicated Infrastructure to Zero Data Loss Autonomous Recovery",
+ "url": "https://docs.oracle.com/en/solutions/back-up-exadata-dedicated-zero-data-loss-recovery/index.html",
+ "type": "reference-architecture",
+ "date": "2025-07",
+ "services": [
+ "exacs"
+ ],
+ "tags": [
+ "database",
+ "ha-dr"
+ ],
+ "summary": "Autonomous Recovery Service for ExaCS backups. Automated backup management with real-time redo protection. Validates backup recoverability automatically.\n",
+ "terraform": null
+ },
+ {
+ "title": "Configure a standby database for disaster recovery using Oracle Exadata Database Service on Cloud@Customer",
+ "url": "https://docs.oracle.com/en/solutions/exadata-cloud-at-customer-standby/index.html",
+ "type": "reference-architecture",
+ "date": "2025-07",
+ "services": [
+ "exacs",
+ "adg"
+ ],
+ "tags": [
+ "database",
+ "ha-dr"
+ ],
+ "summary": "Data Guard standby on ExaCS Cloud@Customer for hybrid DR. On-premises primary with cloud standby, or cloud primary with on-premises standby for data sovereignty requirements.\n",
+ "terraform": null
+ },
+ {
+ "title": "Learn about maximum availability architecture for PeopleSoft",
+ "url": "https://docs.oracle.com/en/solutions/learn-about-maa-for-peoplesoft/index.html",
+ "type": "reference-architecture",
+ "date": "2024-08",
+ "services": [
+ "exacs",
+ "adg",
+ "load-balancer",
+ "compute"
+ ],
+ "tags": [
+ "application",
+ "ha-dr",
+ "peoplesoft"
+ ],
+ "summary": "MAA for PeopleSoft on OCI. Active Data Guard for database HA/DR, multi-instance PIA behind Load Balancer, Process Scheduler on multiple nodes for batch processing resilience.\n",
+ "terraform": null
+ },
+ {
+ "title": "Provision and deploy a maximum availability solution for PeopleSoft on Oracle Cloud",
+ "url": "https://docs.oracle.com/en/solutions/deploy-maa-for-peoplesoft-on-oci/index.html",
+ "type": "reference-architecture",
+ "date": "2024-08",
+ "services": [
+ "exacs",
+ "adg",
+ "load-balancer",
+ "compute"
+ ],
+ "tags": [
+ "application",
+ "ha-dr",
+ "peoplesoft"
+ ],
+ "summary": "Step-by-step PeopleSoft MAA deployment on OCI. Terraform stacks for infrastructure provisioning, Data Guard configuration, and PeopleSoft application tier setup.\n",
+ "terraform": null
+ },
+ {
+ "title": "Run PeopleSoft ERP and Autonomous Database in an HA multicloud deployment",
+ "url": "https://docs.oracle.com/en/solutions/bread-multicloud-on-oci/index.html",
+ "type": "reference-architecture",
+ "date": "2025-02",
+ "services": [
+ "adb-s",
+ "compute",
+ "load-balancer"
+ ],
+ "tags": [
+ "application",
+ "ha-dr",
+ "multicloud",
+ "peoplesoft",
+ "autonomous"
+ ],
+ "summary": "PeopleSoft HA with ADB-S across cloud providers. Application tier on OCI compute with database on ADB-S. Multi-AZ deployment for high availability with automatic failover.\n",
+ "terraform": null
+ }
+]
diff --git a/examples/output-cli-improvements-deliverables/architecture-proposal.pptx b/examples/output-cli-improvements-deliverables/architecture-proposal.pptx
new file mode 100644
index 0000000..cbc4c34
Binary files /dev/null and b/examples/output-cli-improvements-deliverables/architecture-proposal.pptx differ
diff --git a/examples/output-cli-improvements-deliverables/architecture.drawio b/examples/output-cli-improvements-deliverables/architecture.drawio
new file mode 100644
index 0000000..ff6e7e2
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/architecture.drawio
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/output-cli-improvements-deliverables/business-case.pptx b/examples/output-cli-improvements-deliverables/business-case.pptx
new file mode 100644
index 0000000..7b7229a
Binary files /dev/null and b/examples/output-cli-improvements-deliverables/business-case.pptx differ
diff --git a/examples/output-cli-improvements-deliverables/customer-proposal.pdf b/examples/output-cli-improvements-deliverables/customer-proposal.pdf
new file mode 100644
index 0000000..141ef77
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/customer-proposal.pdf
@@ -0,0 +1,289 @@
+%PDF-1.4
+%“Œ‹ž ReportLab Generated PDF document (opensource)
+1 0 obj
+<<
+/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R /F5 12 0 R /F6 15 0 R
+>>
+endobj
+2 0 obj
+<<
+/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
+>>
+endobj
+3 0 obj
+<<
+/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
+>>
+endobj
+4 0 obj
+<<
+/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
+>>
+endobj
+5 0 obj
+<<
+/Contents 22 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+6 0 obj
+<<
+/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font
+>>
+endobj
+7 0 obj
+<<
+/Contents 23 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+8 0 obj
+<<
+/Contents 24 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+9 0 obj
+<<
+/Contents 25 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+10 0 obj
+<<
+/Contents 26 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+11 0 obj
+<<
+/Contents 27 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+12 0 obj
+<<
+/BaseFont /Helvetica-BoldOblique /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font
+>>
+endobj
+13 0 obj
+<<
+/Contents 28 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+14 0 obj
+<<
+/Contents 29 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+15 0 obj
+<<
+/BaseFont /ZapfDingbats /Name /F6 /Subtype /Type1 /Type /Font
+>>
+endobj
+16 0 obj
+<<
+/Contents 30 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+17 0 obj
+<<
+/Contents 31 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+18 0 obj
+<<
+/Contents 32 0 R /MediaBox [ 0 0 612 792 ] /Parent 21 0 R /Resources <<
+/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
+>> /Rotate 0 /Trans <<
+
+>>
+ /Type /Page
+>>
+endobj
+19 0 obj
+<<
+/PageMode /UseNone /Pages 21 0 R /Type /Catalog
+>>
+endobj
+20 0 obj
+<<
+/Author (Carlos Mendoza) /CreationDate (D:20260412224013-03'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260412224013-03'00') /Producer (ReportLab PDF Library - \(opensource\))
+ /Subject (OCI Architecture Proposal) /Title (Mercado Libre \(MELI\) \204 KVS \204 OCI NoSQL Database) /Trapped /False
+>>
+endobj
+21 0 obj
+<<
+/Count 11 /Kids [ 5 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 13 0 R 14 0 R 16 0 R 17 0 R
+ 18 0 R ] /Type /Pages
+>>
+endobj
+22 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 589
+>>
+stream
+Gatn!?#/1K'Sc)J/)JIV0[9I"m)q$K+=pf&@X=:YeQ*FrgWF'=,@`rea*(Y;X:u"/TOS;#o/tG57mqujF'\s@149#D_$.+r\7,fu$o-[okMaa-:+4J`*aqT.uNtP#b6nV7_1i?^f)!jfh=sh;?`pWYr.o=9W/^QnDaga:.:@k0Wm.F%;p*:/+Bn3G.:[+ETq:nM`c3K$b#Zc%,#i8k6'Xek]jl]Wq(&/NfMb.=10N)hUepp5i;Is+3T6u[Q^@#BWc2RqM&8KIG"GJ[I?n5@&2BHZl<9)='^a<*q3j\Jc4\U7rNX6&L1"o]43bnGoP4H6*=9N*W41d\88+1.K`A2U+#GhM[ApM4Su0OpllqXrYOQaX42[WQNZQ$N<0UjTA4I.lRKbFcH&iPW@2HA=Ak(l+$2n&ipQVeC&h4S@$$D])0@?T"(IrAZIoH:G+g";:IHr7"8`DtP5~>endstream
+endobj
+23 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 1866
+>>
+stream
+Gau0D>?BQ=&:WeDbfcTAp(1`UnuZg5BX]R0"%g!!^l,F^7)bKl[V>!0hiW:X#fJ+U*f#bU*&2Io3_%Ga0$r<=bA3>Fb].@<^g_ulSdQ6:.YSQiJ'PZ^f;ht"-51ap9?MN#]+U40b2QC8pSm<]Ll.jW9FFtJj1fYZ*$cPb9!/FMhL!6lCUgto!T"rC]B:U_u3G.50CB1PN(DHqmi\=?/h9O>CfBD8Ym+BHC0u]$#8g-C5-tl$Y!Y,h#Od]YWBQS;`kq^Cerc&?7UP3KT.j0-5lXM.c"u,+!F`cj'pG\j'#9r9+sRn3KNo\8sjPU[kCR[fsFg4V:>'CV"XDk`"T^`Z""#`WI9?SX8/[,8f:sG?3Kt:\uX==!c9he:GsYKhRRUZF0Q0>hgM@OO0QoEVU*M?qQ"-;@ej<$lt9=^r]ptO`AA#'p]VfFZ,?WHQJ*FpRO3ZS+tg<]X=F+E4rB5$fPupZJmE_gEcp-g!nqZKJE7(I)aF'=jL.A`_3!SVoGGLg4i3!YhBhl&Y*q]J^K^Scs1\KPIt3sTPa[?eeK)6gfm;uCNkP7aIecNp[k_6&1qgF8]l]]pK7endstream
+endobj
+24 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 2750
+>>
+stream
+Gau0E>?BQM&q9"FeB=GN+VqZb2J6>987kC8+Yh*Opn]ROm@Hj`7kL-?J,OiH\l]/H_Cdj=@+"DFY,\\jX")(5(I+Mi*@4*lDWp_`8"NL8$h?V3Umk#'HZSi>a;GEB@-!O3&&Oon&,sbM07P&=FF06M0;Ln;:C6]?6i.SHjJW\MA8*9d?O:ejT]_A&E^J(koR#XaP1LXL[c@NsV32R,%jf`9P$''mRP*,AP;MZ3]tM(G*ZrkU5BQK9bA89mHHr2@3ddgRKpp1A=!DN&ECTA0kX(8kl](`pBkSU@,c=_u'X3\I./Wf>>31eH-h'LoLX+6rG:f,DJk%B^4_HTfD'#l:'pJ+_rRQPG5c6fnGA5tETMq(/B&:>lKd3N/Na+gcQC.&SjAO():Yu4<1g34W?I@RTpWoGbmWt<'MAG25<5A)T=iW!iq-CD9(>Y[`mgnVCP7*dU,#\2X:.Ah7'hLc=K2j/V7CW7gV2J$74=d/=aQ,W+L[Hh(0qb^>G0bi(oM4NF/Xsg`7X<1OFflAM+'>hq)78:_DOKm!GhQSCSn1ATep+Idi8Tld9'LX5f\dW)O[&8B+o2`M4QA@;!ofe8due,?-4+.`McuB6p\d.NEJ'ZW\h&6U^`9>D(jfdUF6_]*)qjn2Ba%HM3fFApf$S4bFdn:qoPB\L\D*^K\`u>_`ia++XH:@C)Ye&p,Ya\3$#bpi=YhbotP^rR'2YO6O@DaaWVKhl>aAb3Y(nBVf``;ij1[=?1YRnE+%u1Y3V-hB/[gWBCJnqZ/mK[NEQuqn;/r18of!p25^tB;3gc/A%9pm"=amIWBo+)su9GbdD8Ho$a\F[LoBI!=e,]PF[/FL'IHgh)!_RfH[)c37`5=k^+:n'Yo?)'.I%Q;lr9eDrXmohUWAH%GkHF^;:f)7iiDWk$l!DW"ZfcD6i"hI_RAM?FcN3QReajI&c0m!>F#0%1*lSai2kVou8&DfH*1(j55('Z:r]d%t&$q2+XkX[:/+Q]-2KW'fpbK,fIYl/aZ!?-4RbLt=&;!?tno^WjFC"g!-EuqKL>2q*qSRc4:OB7un"YrNqnqp$p3X^9oCes,[eos>C;7f#U@LB-H=U0uDqa^#M8W\q27\-G$WU"jeWS:Y_0H_6Ds3tZs'J0DWIjSf2s&b'[6`XMKDa[aUXADXADC2gh:+X":atrLD38-]))j0gSa&TZB"RI/jO6VpFj0g8_'gp!jU^21kmYkkU[qMn"i`+![73eK.o`%nh#8qp\f24tZ:u`!!e$n['?Vm"I0(2Ig/aqdr%Z?7BfraoNqsprH3XMW@Pe0jm4FDK4i3bIXIR(?Z@YsM9,+pCd_s])=7;JeCece0#h.%(I]>$dJ[fm!NIX+l_\'2V(aENHJA\T.)O_bgFg\hOf>A<4p7H9)SQ?!FuE]JiS]%8WlJ(?doKDO/[hq>aS6).T\"f+]EY=I]i!og")_mn-@Z^ZK50OWi>5#j])"IuEE^"r;#=fI14^blo!lTqhh]8$Ae>,ZE^hf]3Fc6iSg\Y2deKt6@4"J%j9U<`Bftde&(+&4,$W3[rh+^LVT/oBm=d3YNGT#;qPG9r&tn*aM*tUX0@mA)=$59M<:GXg%)Dh@3Ss-j?__b].$TrZXGU,p>@#0gRRo!W<]B?kp"%>=b\_E8a35D^D(/EYmAeoG=XUM_+#/B:MK`>`.l.)2Edo8^]b`,kjc%BX#R>Hu*,2dKG)YW'U"?s2p!E8qK?@,c]o7P$-c)B)ABtc3'BWFZ8^Sj5*/tULToECS699JQH]!H;1ucJ-ol.]X=M5j0,FMo&S%ErH7OH&^:o3-kA0'UYG4%#7hXF**Ae0$YD+[0R]`<-`T?M`J:NGHHWhCDQc[WWgks9'=a(Xm[4c6117Bcm#N*fJ4=a7HC/Vp'`L1;4Kp)Jf28bmSJ5m,)nA"mi(+=9.,4NkPbk>Jj+RO4p[_tWl\`0O&>?| BQFhk5:*b0dnDYo(dq4StR"(=u[Oa0"58hdj_g!@/=HN_hIllSSsRD\#sihu%Fn`483U4["7"&WnYq;V9ZJ?)"qus0^<^fiP)ZIV1/KICH]A<4?$#(V*5`5&B-:h`j?q59ApBSCjO"T6T(mkAO\]?#a(qb&8An%/a_fhlcNX?*<`sZPrW3.\>p**idC`IVMn<@TTUTC0O!^G59c.6JS_T"%t/Y?d;5MI8@qCe$87D8~>endstream
+endobj
+25 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 1731
+>>
+stream
+Gau0D9ip(1&Dcq.B(_Vd&S&r?bEPgq$T'.iDj?;lMaX[a\ZuVkrUk)>`?@M,5jP!]YTc`84.87"Wt;N*6^n$H%e+oO-Q`U*$F7<.'sRUA3Dp'+"Vi\V)&l2VjJ"p1:$7QaiPQ-K]7*<_/U6;dafh5j]XV9NFO!B`;j/`=[o&]KFY/U&!jdW:+Tu!JcR2YVtdTFIJ[`j!ER"75ZU8m,e(K-?P4Q]'9pKXh>YZ`b8Lb'D#5c&"mfQ%En^k/6^\%H)tPbM&C%;lbZpf/$_#kpmFQc,X)ik9r:ZDL6&)L<^$f'db*N7s4-!naC&X>ZchdPiu3A5-dp93WEF8!p]H7q_WPO215S0$[Aqq%cc]I=8YA,$lJZ":fIk!4lPfoGB^d=M)l`FV6lhmXDXW92%ERmS/7\q%79?$Ft_HL-D1k2ippIV[,VkekhVqQOE)#dJO=)r4nj>8*#k/S!J@[P+jWiJ-S5VR:^>om$l9m-'cCbOrPt'\8Kp[5>`m(Sm#17DcWnZKleL.Opd2LPtsQ+5OAUBI/AV$&tNk<4D-lK5B_]a9:H+s4krHgG_p$5INn9t6\\XrB(AcS^EER2QY8`fEoKn_G^)!F;;lj4$kp;m,\)OJ[jLR0L0nj2Q6Ga.#B@A$sr*pbD@3-50Int39+OS;`en:93E)(gSHL71dl4F&Z!=`1i]>&??taf9M8Je0XYs<5oB*N:LJ;KO8nA7e'[bP3N8h=J0Ug!pSAA?Ja&/co5G+HTo&>qDmZ]@JrDW@j8,i[Hm*k_~>endstream
+endobj
+26 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 1085
+>>
+stream
+GauHJ9lJN8&A@P9Qt&G![3tr967E1>6jq2?Q`s1-W(BT>"i7BTlm-HA,6elu[G$Ua`h)?P1OcS/:*1's!\nL?":U2<3-nZ5X8JuKZgr0A^u`tUe$D!":S,Am)_@WXR+$aU0r$+S:R_-k]IN3Lb\nh$R,cJ4g4nY)Bqf.U^f+]WjHTH_4fL.5?WQAGQD"Ggb_FB5k?eQm1JCV!B.&n8hmVVU+@eRtE'`$B,Y]dXHWjF`B#:4(Nj^,PVp`]$B!e>%Am5b&kW_EEPgerrPRu*!oa+LLlAV*;d92;Uj<%)EP-T*aI#G'q<`&Y0'c$'j%VSWnY.g3*pf'9;c\dWuA8]DS,UngTG&E[`?d:_\CliIa)(@l>^@'^eB!Ph0=G]kPs4+/\\USTT7ckm*[tD=YMtUCB__jZ_ASglLlUMG15TC`)$@OT+"k2%1R3(4F2TWuhhSMs\C1VPWP7%bl/8rr1L$W0eRd7*&1[)bP;%X?1ZVgQbe7H_7HWJ1*U.VF)nJo]hkltTph2(>>nt`j:)oE:HWGY,M10o2:p0JLaWG^coXR'p#%=q)nj&ZqQ-e44"UDT@mWZ*i#AWk3UcRgPIquNk-PX>~>endstream
+endobj
+27 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 1004
+>>
+stream
+GauHLgJSqU&:N^lqB;h)9?4SM*YUn=)DJ*Po!6,I:gaTkV"49Efk#.Lg50Fag4Gm/oJq>f3AnJO3$+gl+3afB&)cZH!$Pci%72i[K._6MX`#fR4BFfMP>3'JVg'NHi_rLecdj(p#%oL2^_gF=VJt5B_"id%R,*9IXc*Z,QkmPA!t:A692N3]Tq=S9gce"@sg+Zrd#LCeW^dYIEOtR%p,\H>/KY02dUsB=AG_9\SN1S@s)MobJBm7`F?iUM96!V%V^\1$9W=?j-&o),C3L;?55ZDe/et[+Fh26bJj.TEEVa.Enuj_Ni1rAO^(APjEe$[rZkE#,oIr<6qGGM-b6`\agIqSN&Rpul&G?b\IM1`P>jN_H5VuI39Y[FUr#uj>Wf!)N%$CHP6qP_6Z&%P`cmS=-e42:aeb0c"tB1J,h7m5i_gr!DQHD^T2[`pa^p\$L*=*L;VcRjEiGkG0d=.r;V`JYTaXM8@mP.GUq/k4AWuB`=YHbhJn3!acNr/ai5(l?kDg0HIJg0us"ea]&O!R=5HaYF)N.:7n+\'";9>-,=043oAaEnC"+-Os#p`T/V[]WFp8#KeLIoE)p58G@;nrK?cj#SDs#2+2/k_CLL8H"rtQMendstream
+endobj
+28 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 2226
+>>
+stream
+Gb!;f>>s99'Ro4HS-Rb>]>7&/]l+$e\(`1(-/6_jjMa([h"m2A*>neS*r2Cs*E&0J%RX`:l'tQ4VP>f@5k'P@s1/6H?j`h#.8U5t>G.FZ_/osLe"_DR'[n8+bs5^7VGmC(Ikg`hiJ>>1;$a`;VJ\X!\o)(T5$T0Ei[c7tF_IqA/\n30$bpMX8/b^,,$H6h@J<'Lk@0FrU[r9Y<"u.]<&`'Z"'gK$Ija?d7q:>/7Dk^r7^B"P6(4U@?T>"CCc9dmu!F2)7^d0Q]:NHiE1-lYP?B]mo'J,si@q`jN:"*%!@CdF&HPD1u^&PH<1:'r6*@,nMV*%nE9I1eg!]]%Dk3o"A5raSCni1=EP6-I'CiI>m/-??h6Vqt5?lL[,K.3hoc6qVs3W9$oDS^l=KkDPJ%dS-3=\XL?nni3Mun.`g^PC3G6Q%?*f:LBUcspb`JT]=Tu7\TO=1i9^qY.=)1nJoq+[*q"q6=Ts2/r+3[qPjdK\84fb\l.N9kLh0\V;Q[-G>+1=T^DQtYJ%tI"$KpecOS)l<8bBi1;]$3ebHYN0Pp=s#[^fT>73oNLmPFn=U#7r--#Fb!T:>d\S7*6`T2\Us=tG':>bDKMsWEJFgSNm(dc'4SILj&:6eI\7i_Y7:IH?o*aV_m,]oqUXH,]jSc6Zk=l]5r.cISs.::dWmk-A>a,ftE_[9Ks;ZOI+m""b50i>uDC8Z%]14^X89dS7ZR\N3:1L5DT9O#W^)84c?/4?AR6cV1668^WsjZ^L)`VAUVbErUmP02A`nO`EdnWd_=,302"PJr]6p6DN;0+H3RN32rfML`M_P1E^<],F&j^R`l^41H_%nM/bF(;Fb=#'hPd<:NJl,'F:7?WW4J79TnY.SF;]@JZ'4j5B8]1u3H!#AjPhq19-mYu4h2;dkN?e+l8fTbs.Nk'cbrX2)I'oZc,kuYHjuOt%d:N`!.J49+TidspOUfm9rtS#B!!FNDO8WaW`6PVHa7h6mQjcDTe(+k@2:/G&CU7%LYu/Y!l<.k)_pu&G;(Nl9IN,[S7KcASendstream
+endobj
+29 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 2442
+>>
+stream
+Gb!#]>BAQ-&q9SYfVabbZ"X9PVfNnqfh'[sZ"*%ZiJ_D10Ek$]GYq&RXulUPB)u=,aV#gYFJe(i#d/^6`U8N;`KGb.Imj;Y`W-fb3."GO:`)uZfS0jqCUb9)rHVpu^1d`b%-lLU_2!?3E;/=I_UD?`NX_T?64An7lVRe[UQV1O\-7Uk1,EaiTn*Z-9o.sn?m$iVG`^2"!`H%o)MKu>].\$;D^`:R5HfCe>5QG?6W*%#`h)IDa[Zh6In'J&b;40`o*Eq0\<]#cL>N`e)P#SVGNsLS.CNXF*K"lIZ-;@L"6kc7XL!)C7?^)*>A,;ecBfFS[d3`&0\b)8pNCf>=Fl<_g-Il1cg,,>@U1f\Y;.$Nc;*=sKomaT^9LoP(HN2=`<+[1*>RgH!IQg*W)eBK6,>3KnWJo)*i:1TFt7:AK-?<)lku[7#3Rd)UQq:]H\s=l1)<>!c3J=1,^X34U,PUghe-^1poRb9dXT5cJ*[aYoV%-73P^3'"cDFlRb=L(Rc%3L2;`he:q`5Bd9U*`r3XInD&/g_5uiQ++Z7flfCWP)d)`A7fM*E!ahh=r@FcF@+$OVqeQ#4E7@;c$42+dMT/@J<3YiC":t4_QVF&1<&7NedO:1)*NUh;Ib;q`LV*2?Q&)^AIqKraIbMP!&r5p)6$/@_fH(*M1CIh/22%WiJ:8#AGAK%a#OGB6+aBVNd_`oZ]A'mRMJV*tNMSAbq(J2o_sN(0Sesq+F6WO(Tn8Wd/o_P9l,^T/%R8_c[?o--p`VGGV_gCknkOXKpi7J]k.s4k4:FS`kDZMpglAJifPWdNfV>,1:HQ6.&J(LFbaO:=dRO(OFe$XcKKSQB`A&OFs<^3L9q\;/U/hq+r1)\BPBtnO2\(e)")7tiNNcSs8%l&^^fo:*T"[LW`B1^&-[EI!>^b7NYe`.V615O>PfVB64b_#>(lK>uc=,ddKH-rI@$#k%UQR38./L#r`7)K8@:sgBu)>D]H?Rft,j:)qqk_Vg.ku;g1=!F6W`PSI3j[64fRUZ:R:0ZWd7^q/ZgAbD84Ou2lQ2XHSs]#8`=8b2"+HE5lh;kZb"<>4lmbF8'6)oVTVB5K$m.]+"&TaaX?jI^iq=[dk'XF[X12\[^XAW)0F>g\.e[>e:&`bflEQMM'N?fm5T@AILQNKgk0M>DY]j>U5.Xq:%RT5bcYumn[J[Vhfa;(+0UT)s)IPej"&AW]!<9EI_i)`KLoKP[Bp`4#s$UckXCe4n/@5YAFV?rE#Yd'/fUubJa#jnc]QOEadG&"ZD6:ejKa*E(j%I?.*-f\D@&6k\YIP--Kn*W\ugob9k!;2DM@=s%Ou[>;c9'a?b`!6m:\?pW#>m,cS_!!t4:D5H==]#q=K(Q@g'qZ7oiM:h?eX0h':rS2n6n*;k$USZ(hhD5X7>#.F!ZDbn_!cT+H)QDou;Pt0/F2GXo[+jY5>Hqme6?=#mm*A)O8$RAt@V&>R?j)OT,D"Tg1e-omql'r_[F\@_ne~>endstream
+endobj
+30 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 2396
+>>
+stream
+Gb!#]968iI'#+6EoP)L:)GpY4Y!"eR1ZO<44-k*QiX8.@1<+\i+DHo1f5Ca6-'eEUHa.^NGai.N?=np`u2\53uEGk5UbF4J@LmQGF*8tGP(:D$:NZq2I)OFZ^SQ@(Cpn^'>EG,$k7iXsT6",qVqhb2E&PbEsRZ>`,NAE.<7XIX,r5i<.m6%*)Wb]jn\ITj,AIB&`4'b[3M7bq@7u?H-Q,_)=GjQ%EDo5iFl9e1#"G^j?TMXN34_5`S^j'D$+0e$<`dsU\:E0iV6HL.)/MB(7t>e+:s(`P6u])gV@_(XR#dXud6&^gi.p),V?QWb3`ZWSG%\>M06\B#>WHIna0bl:g:?C'5H_TC%+,[r[lP34PQHT5tSW8jR[3"?k$#H^/'C]fEr^jR._B,A(YoeP36)g7"ik)Zo@'4B:pOt>0gS['Q.OOR1hF1KjN)^:djao!'(JEgi'eMHA=$U.lGcgh3>Re@2C@),&!ZCK9a#Y#BjKcrq)f,BB:#;D+[lbC0[.L+;[@o5BS=YCQ5kPgiRIi7A3L/2&mF_M8Y238&DEZbUSI\X[E-Meo.ANVL(&n2V@E"I<6gXu!HWeYFQ*11#@s$*_LL__I95mhZqjNEPMPSd3FV$XCDHq,WW121BrM`fg2-B\@f:S?DZ>t!;7f"/Kod.%3]*Uae;@u=!i^$r*,?%n86)!rp[BBL[LWfXW*D"9^^6c2#6SPX@_2#2fs.$H\^C6%EOj0MM"CfY_ZaN/a!?YJBt*<76j^KOWt;IRd1to^oPH)ci:$TS-Mj(KncL7*"roe*.B/k:K1.V]n!WhXV;Kr+/@r+W0mmiP9gNU"3$"diF%d)_SBjbEPq?%kXguQun*kdN*Z:1HC+l5E/5q`1C9g\B4SL$Kna`Lm;$o8shcI_B[_>S?rAB?Z@FepHMj;PTlB8=7[F`*#gDR0kr3jpYpUP.5]C&lP\6=U?=D@!L9l%F+%>(HP0s1/kNbZKYcH^1C$"&h/GBe,^lk8W'780Y:\LSFKpe5nb[h_9>N8#6RZ-Bl`j4J842]Aj^YFN5r"Is.an??A^+%o_/X.qoDJsj#8*sC^%59?'#$dY3]eTK1+(Vfodma)dPH"*aDRL@bq/ohG_I/.$g"B(A=b)l6Gj@+bHn=WSk&&P*hO,%r!B1hd%>>TO6'q[-:*t)3s[CPg4@%K,Or7iC7Z7*nYjE&j)3>NI:7bN'jZ]0mi+-5_`6Onm/9iGpH5m35Hq>Q",J^dt-!u!00Z@`_Th;59j%COPhoeHiO(hD./N)E9C\uiCIYW:E@=i%LV1$ZGp#OtBkN2)Lu)UD$5\?lT+f#$?!k4uMio`NR3jNJ":74e6'#nmb9i6%-QhntbaEC)IJP0JM(IgKc+b;Q\2S+n81hBS/Q>iG$&rd-I,T&*Gjmjm7F/G]m"uYF9k4Q`k6Zld\nN%oK01rgcHuCrMNspZmfuCX](`4JY;+Oo/FCScg6u0p\/NMTm:,4D@U$pRu]NDNlsp#,SrP@M95M/6ErKL2j"e\/$O:2:/Xh0i!rE98ki.0E^PH9qg4JfmX.[76u*P'4O&@T_#V>9ecB6C1Qb#o>bqZjYDluq*e1?2t\)Ep;/Ohf!/WY%VuCK1"q$q"M:ee\)3_X&q*;q3d`hDG(g61n_](g8K7#)F\)GaSfPD,JW"n&d=Gl2Z`bDu3;O^](?=K~>endstream
+endobj
+31 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 454
+>>
+stream
+GatU.bAP3?&A7ljk*Yd+!J>r5)^SL[7"+luMebDR>K:VQXK#8b^-EYq&+rmFfO`%Gc2\:&%2RNB%:#g@S1lXWF9s:M5WZeBNSYnS1lVqLiCZZ'K&:Q1gdE,(;$/8?A:c\7e01m_)"i")ZPequA44A!)Z_-";i(Ws9M?qgPm=O";BQC@2$W[#k#p)@<^#+KMnsd'RkBF_Ub>7l4QdlL8YV@Mn>L2i@Q]i8PIS6$)FV00HrO>XF!a9B<&fSOpk(W+(hL-n2jfq+d=,[%`i[MV]#:Ob])pGnBsZ(8G58SQjbZf%5G78;DZ~>endstream
+endobj
+32 0 obj
+<<
+/Filter [ /ASCII85Decode /FlateDecode ] /Length 772
+>>
+stream
+GatU/?#LB2&:Dg-fJc\B\``k(8#TT>C.4ijfGE_`QPf/W@/GiPmo__D8Q(+m:aFe8G%PC,)\*1&G[.imi-c!"2]R&LD%AaA3OF!Lh9LJn=B-_OP6+CX]\p/ND^g)40MCBf9LFq0$?TWq>L!BmF0#X[,7O))`Wn0]O4`b"MQY&DauA2FU29!VF:6a4LdK;U8^lqGCYs11fAc=j=_*MkoT'a!QJ0TpJ-eG8RuG8mRe8<^DC!dp]^3?ZHI$>NDc>J@\pX5,p%-/U.]Ubf3q32\>Wc]fafe%<97W+3'#7]5iJd3F#QG%iKiHJrk>A5DifGKsMULJd%"kqBrM,As:`b\.N$^Z*Fpm%9go4"E/@a_nDZQ$/.kZ.PaLH3MHBdN5ea$/U3#r1GmpDIJ5;XVe"haJnbR'qr8qR_c*j!VVb8d^5#Yr!o4endstream
+endobj
+xref
+0 33
+0000000000 65535 f
+0000000061 00000 n
+0000000144 00000 n
+0000000251 00000 n
+0000000366 00000 n
+0000000478 00000 n
+0000000673 00000 n
+0000000750 00000 n
+0000000945 00000 n
+0000001140 00000 n
+0000001335 00000 n
+0000001531 00000 n
+0000001727 00000 n
+0000001847 00000 n
+0000002043 00000 n
+0000002239 00000 n
+0000002323 00000 n
+0000002519 00000 n
+0000002715 00000 n
+0000002911 00000 n
+0000002981 00000 n
+0000003315 00000 n
+0000003446 00000 n
+0000004126 00000 n
+0000006084 00000 n
+0000008926 00000 n
+0000010749 00000 n
+0000011926 00000 n
+0000013022 00000 n
+0000015340 00000 n
+0000017874 00000 n
+0000020362 00000 n
+0000020907 00000 n
+trailer
+<<
+/ID
+[<1a6aa1302ad6ba2dca92b79c88f91ea9><1a6aa1302ad6ba2dca92b79c88f91ea9>]
+% ReportLab generated PDF document -- digest (opensource)
+
+/Info 20 0 R
+/Root 19 0 R
+/Size 33
+>>
+startxref
+21770
+%%EOF
diff --git a/examples/output-cli-improvements-deliverables/ecal-score-full.json b/examples/output-cli-improvements-deliverables/ecal-score-full.json
new file mode 100644
index 0000000..850dc18
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/ecal-score-full.json
@@ -0,0 +1,175 @@
+{
+ "summary": {
+ "total_artefacts": 18,
+ "mentioned": 3,
+ "likely_relevant": 1,
+ "possibly_relevant": 5,
+ "not_covered": 9,
+ "skill_auto_generate": 5,
+ "skill_assist": 3
+ },
+ "artefacts": [
+ {
+ "id": "DEF-01",
+ "name": "Customer Profile",
+ "phase": "define",
+ "step": "ideate",
+ "skill_support": "assist",
+ "customer_facing": false,
+ "relevance": "mentioned"
+ },
+ {
+ "id": "DEF-02",
+ "name": "Strategy Map",
+ "phase": "define",
+ "step": "ideate",
+ "skill_support": "assist",
+ "customer_facing": false,
+ "relevance": "mentioned"
+ },
+ {
+ "id": "DEF-03",
+ "name": "Point of View (PoV)",
+ "phase": "define",
+ "step": "ideate",
+ "skill_support": "auto_generate",
+ "customer_facing": false,
+ "relevance": "possibly_relevant"
+ },
+ {
+ "id": "DEF-04",
+ "name": "Ideation Workshop",
+ "phase": "define",
+ "step": "ideate",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DEF-05",
+ "name": "Business Case",
+ "phase": "define",
+ "step": "ideate",
+ "skill_support": "auto_generate",
+ "customer_facing": true,
+ "relevance": "mentioned"
+ },
+ {
+ "id": "DEF-06",
+ "name": "Value Story",
+ "phase": "define",
+ "step": "ideate",
+ "skill_support": "auto_generate",
+ "customer_facing": true,
+ "relevance": "possibly_relevant"
+ },
+ {
+ "id": "DEF-07",
+ "name": "Value Story Delivery",
+ "phase": "define",
+ "step": "validate",
+ "skill_support": "assist",
+ "customer_facing": true,
+ "relevance": "possibly_relevant"
+ },
+ {
+ "id": "DEF-08",
+ "name": "Joint Engagement Plan (JEP)",
+ "phase": "define",
+ "step": "plan",
+ "skill_support": "auto_generate",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DES-35",
+ "name": "Solution Proposal",
+ "phase": "design",
+ "step": "confirm",
+ "skill_support": "auto_generate",
+ "customer_facing": true,
+ "relevance": "possibly_relevant"
+ },
+ {
+ "id": "DES-36",
+ "name": "Partner / Services Proposal",
+ "phase": "design",
+ "step": "confirm",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DES-37",
+ "name": "RFx Response",
+ "phase": "design",
+ "step": "confirm",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "possibly_relevant"
+ },
+ {
+ "id": "DES-38",
+ "name": "Commercial Agreement",
+ "phase": "design",
+ "step": "confirm",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DEL-17",
+ "name": "Cloud Operating Framework",
+ "phase": "deliver",
+ "step": "operate",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DEL-18",
+ "name": "OCI Operationalization Framework",
+ "phase": "deliver",
+ "step": "operate",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DEL-19",
+ "name": "Retrospective",
+ "phase": "deliver",
+ "step": "improve",
+ "skill_support": "auto_generate",
+ "customer_facing": false,
+ "relevance": "none"
+ },
+ {
+ "id": "DEL-20",
+ "name": "Value Realization",
+ "phase": "deliver",
+ "step": "improve",
+ "skill_support": "auto_generate",
+ "customer_facing": true,
+ "relevance": "likely_relevant"
+ },
+ {
+ "id": "DEL-21",
+ "name": "Customer Success Story",
+ "phase": "deliver",
+ "step": "improve",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ },
+ {
+ "id": "DEL-22",
+ "name": "Improvement Actions",
+ "phase": "deliver",
+ "step": "improve",
+ "skill_support": "reference",
+ "customer_facing": true,
+ "relevance": "none"
+ }
+ ]
+}
diff --git a/examples/output-cli-improvements-deliverables/exacs-bom-appca.xlsx b/examples/output-cli-improvements-deliverables/exacs-bom-appca.xlsx
new file mode 100644
index 0000000..68b0ab1
Binary files /dev/null and b/examples/output-cli-improvements-deliverables/exacs-bom-appca.xlsx differ
diff --git a/examples/output-cli-improvements-deliverables/exacs-bom.xlsx b/examples/output-cli-improvements-deliverables/exacs-bom.xlsx
new file mode 100644
index 0000000..b292e0b
Binary files /dev/null and b/examples/output-cli-improvements-deliverables/exacs-bom.xlsx differ
diff --git a/examples/output-cli-improvements-deliverables/exacs-x11m-bom-appca.xlsx b/examples/output-cli-improvements-deliverables/exacs-x11m-bom-appca.xlsx
new file mode 100644
index 0000000..05ff5ba
Binary files /dev/null and b/examples/output-cli-improvements-deliverables/exacs-x11m-bom-appca.xlsx differ
diff --git a/examples/output-cli-improvements-deliverables/exacs-x11m-bom.xlsx b/examples/output-cli-improvements-deliverables/exacs-x11m-bom.xlsx
new file mode 100644
index 0000000..f06a12a
Binary files /dev/null and b/examples/output-cli-improvements-deliverables/exacs-x11m-bom.xlsx differ
diff --git a/examples/output-cli-improvements-deliverables/findings-search-adb.json b/examples/output-cli-improvements-deliverables/findings-search-adb.json
new file mode 100644
index 0000000..a9b1a77
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/findings-search-adb.json
@@ -0,0 +1,125 @@
+[
+ {
+ "id": "FF-202603-008",
+ "date": "2026-03-14",
+ "contributor": {
+ "name": "Diego Cabrera",
+ "team": "Field Architecture",
+ "client": "Vector Search Customer",
+ "context": "AI Vector Search engagement",
+ "confidence": "validated"
+ },
+ "product": "ADB-S",
+ "version": "23ai",
+ "severity": "HIGH",
+ "category": "limitation",
+ "summary": "Distributed HNSW indexes not available \u2014 100M+ vector search requires partitioning workaround",
+ "detail": "ADB-S AI Vector Search supports HNSW indexes but they are node-local on\nmulti-node RAC (\u226564 ECPUs). At 100M+ vectors, queries on a 2-node RAC\nmiss vectors on the other node. Results are either incomplete or require\ncross-node scan that blows P95 >200ms.\n\nWorkaround from internal DB PM team: hash-partition vector table on\nvector_id, create partition-local HNSW index. Queries hit single\npartition on single node, maintaining <50ms at 100M scale.\n\nRequires table redesign \u2014 cannot be applied to existing unpartitioned\nvector tables without data reload.\n",
+ "workaround": "Hash-partition vector table by vector_id. Create HNSW index as LOCAL\n(partition-level). Ensure partition pruning in queries.\nSource: internal DB PM team contact [name].\nValidated on ADB-S 23ai with 120M vectors, P95 = 38ms on 64 ECPU.\n",
+ "oracle_sr": "",
+ "status": "acknowledged",
+ "resolved_date": null,
+ "resolution": null,
+ "affects_matrix": "AI Vector Search (HNSW)",
+ "tags": [
+ "vector-search",
+ "hnsw",
+ "distributed",
+ "rac",
+ "partitioning",
+ "100m",
+ "latency",
+ "ai-vector-search"
+ ]
+ },
+ {
+ "id": "FF-202603-002",
+ "date": "2026-03-08",
+ "contributor": {
+ "name": "Diego Cabrera",
+ "team": "Field Architecture",
+ "client": "Strategic Migration Customer",
+ "context": "Production migration",
+ "confidence": "validated"
+ },
+ "product": "ADB-S Dedicated Elastic Pool (DEP)",
+ "version": "23ai",
+ "severity": "HIGH",
+ "category": "limitation",
+ "summary": "ADG must be disabled before joining Dedicated Elastic Pool",
+ "detail": "When attempting to add an ADB-S instance with active Autonomous Data Guard\nto a Dedicated Elastic Pool, the operation fails. ADG must be explicitly\ndisabled before the instance can join the DEP. This is a known issue\nacknowledged by Oracle but not prominently documented.\n\nImpact: requires a brief HA gap during DEP onboarding. Must coordinate\nwith the customer's change window.\n",
+ "workaround": "Disable ADG \u2192 join DEP \u2192 re-enable ADG. Plan for ~15 min HA gap.",
+ "oracle_sr": "",
+ "status": "acknowledged",
+ "resolved_date": null,
+ "resolution": null,
+ "affects_matrix": "Elastic Pool Membership",
+ "tags": [
+ "dep",
+ "adg",
+ "elastic-pool",
+ "ha",
+ "limitation"
+ ]
+ },
+ {
+ "id": "FF-202603-004",
+ "date": "2026-03-01",
+ "contributor": {
+ "name": "Diego Cabrera",
+ "team": "Field Architecture",
+ "client": "Strategic Migration Customer",
+ "context": "Production migration",
+ "confidence": "validated"
+ },
+ "product": "ADB-S",
+ "version": "23ai",
+ "severity": "MEDIUM",
+ "category": "gotcha",
+ "summary": "DEP provisioning takes days to weeks depending on capacity",
+ "detail": "Dedicated Elastic Pool provisioning is NOT instant like ADB-S Serverless.\nIt requires physical Exadata infrastructure allocation. Lead time varies\nfrom 3 days (if capacity available in region) to 2-3 weeks (if capacity\nneeds to be provisioned).\n\nBilling starts only at AVAILABLE state, not at request time.\nCo-location of existing ADB-S instances happens at the next maintenance\nwindow after joining, not immediately.\n",
+ "workaround": "Request DEP early in the project timeline. Don't make it a critical-path dependency in week 1.",
+ "oracle_sr": "",
+ "status": "acknowledged",
+ "resolved_date": null,
+ "resolution": null,
+ "affects_matrix": "Elastic Pool Membership",
+ "tags": [
+ "dep",
+ "provisioning",
+ "lead-time",
+ "capacity",
+ "planning"
+ ]
+ },
+ {
+ "id": "FF-202603-007",
+ "date": "2026-02-10",
+ "contributor": {
+ "name": "Diego Cabrera",
+ "team": "Field Architecture",
+ "client": "Pepe SRL",
+ "context": "Discovery engagement",
+ "confidence": "validated"
+ },
+ "product": "ADB-S",
+ "version": "23ai",
+ "severity": "INFO",
+ "category": "gotcha",
+ "summary": "ADB-S Serverless RAC node split threshold at 64 ECPUs",
+ "detail": "ADB-S Serverless runs on 1 RAC node at \u226424 ECPUs and splits to 2 nodes\nat 64 ECPUs (16 cpu_count each). The gv$instance view only shows nodes\nwhere the PDB is open.\n\nThis affects connection pooling behavior and session distribution.\nApplications should use UCP or JDBC connection pool with FAN events\nto handle the multi-node scenario properly.\n",
+ "workaround": "Ensure connection pool configuration handles multi-node. Use UCP with FAN.",
+ "oracle_sr": "",
+ "status": "acknowledged",
+ "resolved_date": null,
+ "resolution": null,
+ "affects_matrix": null,
+ "tags": [
+ "adb-s",
+ "rac",
+ "ecpu",
+ "node-split",
+ "connection-pooling"
+ ]
+ }
+]
diff --git a/examples/output-cli-improvements-deliverables/findings-stats.json b/examples/output-cli-improvements-deliverables/findings-stats.json
new file mode 100644
index 0000000..1b7945d
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/findings-stats.json
@@ -0,0 +1,26 @@
+{
+ "total": 8,
+ "by_severity": {
+ "HIGH": 2,
+ "MEDIUM": 4,
+ "LOW": 1,
+ "INFO": 1
+ },
+ "by_product": {
+ "ADB-S": 4,
+ "OCI CLI": 1,
+ "ADB-S Dedicated Elastic Pool (DEP)": 2,
+ "OCI DMS": 1
+ },
+ "by_status": {
+ "acknowledged": 5,
+ "open": 2,
+ "monitoring": 1
+ },
+ "by_category": {
+ "limitation": 3,
+ "bug": 1,
+ "undocumented": 1,
+ "gotcha": 3
+ }
+}
diff --git a/examples/output-cli-improvements-deliverables/kb-search-adb.json b/examples/output-cli-improvements-deliverables/kb-search-adb.json
new file mode 100644
index 0000000..d00ffdc
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/kb-search-adb.json
@@ -0,0 +1,206 @@
+[
+ {
+ "file": "kb/architecture-center/catalog.yaml",
+ "matches": [
+ {
+ "line": 25,
+ "text": "- title: \"Deploy Oracle Autonomous Database on Oracle Database@Azure\""
+ },
+ {
+ "line": 267,
+ "text": "- title: \"Deploy Oracle Autonomous Database on Oracle Database@Google Cloud\""
+ },
+ {
+ "line": 427,
+ "text": "- title: \"Migrate on-premises Oracle Database to Autonomous Database\""
+ },
+ {
+ "line": 463,
+ "text": "- title: \"Migrate a PeopleSoft database to Oracle Autonomous Database\""
+ },
+ {
+ "line": 623,
+ "text": "- title: \"Move Oracle Forms applications to Oracle APEX on an Oracle Autonomous "
+ },
+ {
+ "line": 635,
+ "text": "- title: \"Stream Kafka topics to Oracle Autonomous Database using Oracle Integra"
+ },
+ {
+ "line": 795,
+ "text": "- title: \"Implement Oracle Data Safe for Exadata and Autonomous Databases\""
+ },
+ {
+ "line": 1140,
+ "text": "- title: \"Deploy a microservices-based application in Kubernetes connected to an"
+ },
+ {
+ "line": 1226,
+ "text": "- title: \"Deploy JD Edwards EnterpriseOne with Oracle Autonomous Database on Ded"
+ },
+ {
+ "line": 1262,
+ "text": "- title: \"Run PeopleSoft ERP and Autonomous Database in an HA multicloud deploym"
+ }
+ ]
+ },
+ {
+ "file": "kb/competitive/aws-mapping.yaml",
+ "matches": [
+ {
+ "line": 32,
+ "text": "- \"Autonomous Database \u2014 no equivalent in AWS\""
+ },
+ {
+ "line": 81,
+ "text": "- \"Data Safe included free for Autonomous Database\""
+ }
+ ]
+ },
+ {
+ "file": "kb/competitive/common-objections.yaml",
+ "matches": [
+ {
+ "line": 12,
+ "text": "- Only cloud with Autonomous Database"
+ }
+ ]
+ },
+ {
+ "file": "kb/patterns/database-ha-adb-s.yaml",
+ "matches": [
+ {
+ "line": 2,
+ "text": "# Composable architecture block for Autonomous Database Serverless HA"
+ },
+ {
+ "line": 10,
+ "text": "Autonomous Database Serverless (ADB-S) provides built-in high availability throu"
+ }
+ ]
+ },
+ {
+ "file": "kb/pricing/oci-sku-catalog.yaml",
+ "matches": [
+ {
+ "line": 462,
+ "text": "display_name: Oracle Cloud Infrastructure - Autonomous Database & Exadata"
+ },
+ {
+ "line": 465,
+ "text": "product: Oracle Autonomous Database (ATP or ADW) - ECPU (2 ECPU minimum)"
+ },
+ {
+ "line": 471,
+ "text": "product: Oracle Autonomous Database (ATP or ADW) - BYOL ECPU (2 ECPU minimum)"
+ },
+ {
+ "line": 477,
+ "text": "product: Oracle Autonomous Database Storage for Transaction Processing (ATP), us"
+ },
+ {
+ "line": 483,
+ "text": "product: Oracle Autonomous Database Storage for Data Warehouse (ADW), using ECPU"
+ },
+ {
+ "line": 489,
+ "text": "product: Oracle Autonomous Database Backup Storage (ATP or ADW), using ECPU"
+ }
+ ]
+ },
+ {
+ "file": "kb/services/adb-dedicated.yaml",
+ "matches": [
+ {
+ "line": 3,
+ "text": "service: Autonomous Database Dedicated (ADB-D)"
+ },
+ {
+ "line": 8,
+ "text": "what: Autonomous Database on dedicated Exadata infrastructure. Provides isolatio"
+ }
+ ]
+ },
+ {
+ "file": "kb/services/adb-elastic-pool.yaml",
+ "matches": [
+ {
+ "line": 3,
+ "text": "service: Autonomous Database Serverless Elastic Pool"
+ }
+ ]
+ },
+ {
+ "file": "kb/services/adb-serverless.yaml",
+ "matches": [
+ {
+ "line": 1,
+ "text": "# OCI Service: Autonomous Database Serverless (ADB-S)"
+ },
+ {
+ "line": 5,
+ "text": "name: \"Autonomous Database Serverless (ADB-S)\""
+ },
+ {
+ "line": 38,
+ "text": "name: \"Autonomous Database Serverless (ADB-S)\""
+ },
+ {
+ "line": 47,
+ "text": "name: \"Autonomous Database on Dedicated Exadata (ADB-D)\""
+ }
+ ]
+ },
+ {
+ "file": "kb/services/database-at-aws.yaml",
+ "matches": [
+ {
+ "line": 32,
+ "text": "notes: \"Autonomous Database on dedicated Exadata \u2014 NOT serverless\""
+ },
+ {
+ "line": 37,
+ "text": "- name: \"Autonomous Database Serverless (ADB-S)\""
+ }
+ ]
+ },
+ {
+ "file": "kb/services/exadata-cloud.yaml",
+ "matches": [
+ {
+ "line": 20,
+ "text": "- \"Small workloads under 16 OCPUs \u2014 use Autonomous Database Serverless (ADB-S) o"
+ }
+ ]
+ },
+ {
+ "file": "kb/services/oci-networking-core.yaml",
+ "matches": [
+ {
+ "line": 46,
+ "text": "description: \"Private pathway from a VCN to supported OCI services (e.g., Object"
+ },
+ {
+ "line": 84,
+ "text": "description: \"Resources in private subnets cannot reach OCI services (Object Sto"
+ }
+ ]
+ },
+ {
+ "file": "kb/well-architected/security-compliance.yaml",
+ "matches": [
+ {
+ "line": 148,
+ "text": "description: \"Private endpoints for Autonomous Database\""
+ },
+ {
+ "line": 150,
+ "text": "pass_if: \"Autonomous Database configured with private endpoints\""
+ },
+ {
+ "line": 151,
+ "text": "gap_if: \"Autonomous Database using public endpoints\""
+ }
+ ]
+ }
+]
diff --git a/examples/output-cli-improvements-deliverables/opt06-feature-auto-indexing.json b/examples/output-cli-improvements-deliverables/opt06-feature-auto-indexing.json
new file mode 100644
index 0000000..586beb4
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/opt06-feature-auto-indexing.json
@@ -0,0 +1,50 @@
+[
+ {
+ "feature": "Auto Indexing",
+ "category": "auto_management",
+ "compatibility": [
+ {
+ "deployment": "ADB-S Serverless",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "Creates indexes HIDDEN by default. Instantly reversible. DML costing prevents over-indexing. Deduplication with manually created indexes."
+ },
+ {
+ "deployment": "ADB-S Serverless",
+ "version": "26ai",
+ "status": "GA",
+ "notes": "Improved in 26ai \u2014 better cost model, fewer false positives."
+ },
+ {
+ "deployment": "ADB-S Elastic Pool",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "Same as ADB-S Serverless."
+ },
+ {
+ "deployment": "ADB Dedicated Elastic Pool (DEP)",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "Available but less tested in DEP."
+ },
+ {
+ "deployment": "ADB on Dedicated Exadata",
+ "version": "23ai",
+ "status": "GA",
+ "notes": "Fully supported."
+ },
+ {
+ "deployment": "Exadata Cloud Service",
+ "version": "23ai",
+ "status": "NOT_AVAIL",
+ "notes": "Not an ADB feature. Use manual indexing."
+ },
+ {
+ "deployment": "Base DB Service (EE)",
+ "version": "23ai",
+ "status": "NOT_AVAIL",
+ "notes": ""
+ }
+ ]
+ }
+]
diff --git a/examples/output-cli-improvements-deliverables/opt06-feature-vector-search.json b/examples/output-cli-improvements-deliverables/opt06-feature-vector-search.json
new file mode 100644
index 0000000..565bfaa
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/opt06-feature-vector-search.json
@@ -0,0 +1,86 @@
+[
+ {
+ "feature": "AI Vector Search (HNSW)",
+ "category": "developer",
+ "compatibility": [
+ {
+ "deployment": "ADB-S Serverless",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "HNSW indexes are node-local on multi-node RAC. Works well up to\n~50M vectors on single node (\u226424 ECPUs). At 100M+ on multi-node\n(\u226564 ECPUs), requires hash-partitioned vector table with LOCAL\nHNSW index for <50ms P95. See FF-202603-008.\n"
+ },
+ {
+ "deployment": "ADB-S Serverless",
+ "version": "26ai",
+ "status": "UNTESTED",
+ "notes": "Check if distributed HNSW is added in 26ai. Expected to remain node-local."
+ },
+ {
+ "deployment": "ADB-S Elastic Pool",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "Same node-local limitation as ADB-S Serverless."
+ },
+ {
+ "deployment": "ADB on Dedicated Exadata",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "Same limitation. Dedicated infra doesn't change HNSW locality."
+ },
+ {
+ "deployment": "Exadata Cloud Service",
+ "version": "23ai",
+ "status": "GA_CAVEAT",
+ "notes": "RAC with HNSW has same node-local constraint. Partitioning workaround applies."
+ },
+ {
+ "deployment": "Base DB Service (EE)",
+ "version": "23ai",
+ "status": "NOT_AVAIL",
+ "notes": "AI Vector Search requires 23ai. Available in EE but HNSW performance limited without Exadata storage."
+ }
+ ]
+ },
+ {
+ "feature": "AI Vector Search (IVF)",
+ "category": "developer",
+ "compatibility": [
+ {
+ "deployment": "ADB-S Serverless",
+ "version": "23ai",
+ "status": "GA",
+ "notes": "IVF (Inverted File) indexes ARE distributed across RAC nodes.\nWorks at 100M+ scale without partitioning. However, IVF has\nlower recall than HNSW at same latency budget. Trade-off:\nIVF for scale without redesign, HNSW+partitioning for best recall.\n"
+ },
+ {
+ "deployment": "ADB-S Serverless",
+ "version": "26ai",
+ "status": "UNTESTED",
+ "notes": ""
+ },
+ {
+ "deployment": "ADB-S Elastic Pool",
+ "version": "23ai",
+ "status": "GA",
+ "notes": "Same as ADB-S Serverless."
+ },
+ {
+ "deployment": "ADB on Dedicated Exadata",
+ "version": "23ai",
+ "status": "GA",
+ "notes": ""
+ },
+ {
+ "deployment": "Exadata Cloud Service",
+ "version": "23ai",
+ "status": "GA",
+ "notes": "IVF distributed across RAC nodes. Exadata storage accelerates scans."
+ },
+ {
+ "deployment": "Base DB Service (EE)",
+ "version": "23ai",
+ "status": "NOT_AVAIL",
+ "notes": "AI Vector Search requires 23ai. Available in EE but performance limited without Exadata."
+ }
+ ]
+ }
+]
diff --git a/examples/output-cli-improvements-deliverables/opt07-competitive-adb-vs-aws-azure.json b/examples/output-cli-improvements-deliverables/opt07-competitive-adb-vs-aws-azure.json
new file mode 100644
index 0000000..6dd8aeb
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/opt07-competitive-adb-vs-aws-azure.json
@@ -0,0 +1,46 @@
+{
+ "query": "ADB-S vs AWS Aurora / Azure SQL",
+ "aws_database_mappings": [],
+ "azure_database_mappings": [
+ {
+ "oci": "ADB-S",
+ "azure": "Azure SQL Managed Instance / Azure Database for PostgreSQL",
+ "oci_advantage": "ADB-S runs actual Oracle Database. Azure SQL MI is SQL Server only.\nNo native Oracle database service on Azure.\nOracle on Azure VMs loses Exadata performance and autonomous operations.\n",
+ "azure_advantage": "Native SQL Server support if migrating from SQL Server."
+ },
+ {
+ "oci": "ExaCS",
+ "azure": "Oracle Database@Azure (ExaCS in Azure datacenters)",
+ "notes": "Oracle Database@Azure is ExaCS running in Azure datacenters with\ndirect connectivity to Azure services. Available in select regions.\nThis is a partnership offering, not a competitive comparison.\n"
+ }
+ ],
+ "common_objections": [
+ {
+ "id": "OBJ-001",
+ "category": "market",
+ "objection": "OCI is a niche cloud \u2014 AWS/Azure are the leaders",
+ "response": "OCI is the #4 cloud globally and growing fastest in database PaaS.\nFor Oracle database workloads specifically, OCI is the leader:\n- Only cloud with Exadata hardware\n- Only cloud with Autonomous Database\n- Best Oracle license portability\nThe question isn't \"best cloud overall\" but \"best cloud for this workload.\"\n",
+ "evidence": "Gartner, IDC reports on cloud database market"
+ },
+ {
+ "id": "OBJ-002",
+ "category": "risk",
+ "objection": "What if Oracle EOLs or changes OCI pricing?",
+ "response": "OCI pricing has only decreased since launch. Oracle's cloud investment\nis $50B+ in CapEx. OCI is Oracle's strategic platform \u2014 not a side project.\nContractual pricing protection exists via Annual Flex and Universal Credits.\n",
+ "mitigation": "Use annual flex contracts with pricing guarantees"
+ },
+ {
+ "id": "OBJ-003",
+ "category": "skills",
+ "objection": "Our team has no OCI experience",
+ "response": "ADB-S reduces DBA workload by 80% (no patching, tuning, scaling).\nOCI console follows familiar cloud patterns. Terraform provider is mature.\nOracle provides free training and certification. Typical ramp-up is 2-4 weeks.\n",
+ "mitigation": "Include training budget in migration plan"
+ },
+ {
+ "id": "OBJ-004",
+ "category": "features",
+ "objection": "OCI doesn't have as many services as AWS",
+ "response": "True for breadth (AWS has 200+ services). For database and enterprise\nworkloads, OCI feature parity is strong. Key services are GA:\n- Compute, networking, storage, Kubernetes, serverless\n- Monitoring, logging, IAM, security\n- Queue, Streaming, API Gateway\nWhat matters is whether OCI has what *you* need, not a raw count.\n"
+ }
+ ]
+}
diff --git a/examples/output-cli-improvements-deliverables/opt11-finding-verify.json b/examples/output-cli-improvements-deliverables/opt11-finding-verify.json
new file mode 100644
index 0000000..33756e7
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/opt11-finding-verify.json
@@ -0,0 +1,33 @@
+[
+ {
+ "id": "FF-202604-001",
+ "date": "2026-04-12",
+ "contributor": {
+ "name": "Test SA",
+ "team": "Validation",
+ "date": "2026-04-12",
+ "confidence": "observed",
+ "client": "Test Client",
+ "context": "CLI validation"
+ },
+ "reported_by": "Test SA",
+ "product": "OCI Queue",
+ "version": "GA",
+ "severity": "LOW",
+ "category": "gotcha",
+ "summary": "Queue message visibility timeout resets on failed processing \u2014 must handle idempotently",
+ "detail": "When a consumer fails to process a message within the visibility timeout, the message becomes visible again. If the consumer partially processed it, you get duplicate processing.",
+ "workaround": "Implement idempotent consumers. Use message deduplication ID.",
+ "oracle_sr": "",
+ "status": "open",
+ "resolved_date": null,
+ "resolution": null,
+ "affects_matrix": null,
+ "tags": [
+ "queue",
+ "messaging",
+ "idempotency"
+ ],
+ "confirmations": []
+ }
+]
diff --git a/examples/output-cli-improvements-deliverables/opt11-report-finding.txt b/examples/output-cli-improvements-deliverables/opt11-report-finding.txt
new file mode 100644
index 0000000..2d47447
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/opt11-report-finding.txt
@@ -0,0 +1,5 @@
+WARNING: Unknown tag 'queue'. Not in taxonomy.
+WARNING: Unknown tag 'messaging'. Did you mean 'sizing'?
+WARNING: Unknown tag 'idempotency'. Did you mean 'latency'?
+
+Added: FF-202604-001 [LOW] Queue message visibility timeout resets on failed processing — must handle idempotently
diff --git a/examples/output-cli-improvements-deliverables/wa-scorecard-no-profile.yaml b/examples/output-cli-improvements-deliverables/wa-scorecard-no-profile.yaml
new file mode 100644
index 0000000..92b0635
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/wa-scorecard-no-profile.yaml
@@ -0,0 +1,174 @@
+well_architected_scorecard:
+ overall_status: GAPS_IDENTIFIED
+ generated_date: '2026-04-12T21:26:00.300204'
+ architecture_name: examples/meli-kvs-wa-architecture.yaml
+ customer: Unknown
+ summary:
+ total_checks: 23
+ total_passed: 18
+ total_gaps: 5
+ high_severity_gaps: 2
+ medium_severity_gaps: 1
+ low_severity_gaps: 2
+ pillars:
+ security_compliance:
+ status: GAPS_IDENTIFIED
+ checks_passed: 12
+ checks_total: 15
+ categories:
+ identity_access:
+ passed: 3
+ total: 4
+ gaps:
+ - check_id: SEC-IAM-003
+ area: Identity & Access
+ finding: no mention of admin account restrictions
+ severity: HIGH
+ recommendation: separate admin groups defined for operations
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ resource_isolation:
+ passed: 3
+ total: 3
+ gaps: []
+ database_security:
+ passed: 0
+ total: 0
+ gaps: []
+ data_protection:
+ passed: 1
+ total: 2
+ gaps:
+ - check_id: SEC-DAT-001
+ area: Data Protection
+ finding: any storage service without encryption at rest
+ severity: HIGH
+ recommendation: encryption at rest specified for all storage services
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ network_security:
+ passed: 3
+ total: 3
+ gaps: []
+ monitoring_audit:
+ passed: 2
+ total: 3
+ gaps:
+ - check_id: SEC-MON-003
+ area: Monitoring & Audit
+ finding: no VCN Flow Logs configured
+ severity: MEDIUM
+ recommendation: VCN Flow Logs enabled for critical subnets
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ reliability_resilience:
+ status: PASS_WITH_RECOMMENDATIONS
+ checks_passed: 0
+ checks_total: 1
+ categories:
+ scalability:
+ passed: 0
+ total: 1
+ gaps:
+ - check_id: REL-SCL-002
+ area: Scalability
+ finding: no service limit review planned
+ severity: LOW
+ recommendation: service limit review mentioned in deployment plan
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/reliable-and-resilient-cloud-topology-practices1.html
+ fault_tolerant_networking:
+ passed: 0
+ total: 0
+ gaps: []
+ data_backup:
+ passed: 0
+ total: 0
+ gaps: []
+ data_replication:
+ passed: 0
+ total: 0
+ gaps: []
+ disaster_recovery:
+ passed: 0
+ total: 0
+ gaps: []
+ performance_cost:
+ status: PASS
+ checks_passed: 2
+ checks_total: 2
+ categories:
+ compute_sizing:
+ passed: 0
+ total: 0
+ gaps: []
+ storage_strategy:
+ passed: 0
+ total: 0
+ gaps: []
+ network_tuning:
+ passed: 0
+ total: 0
+ gaps: []
+ cost_management:
+ passed: 2
+ total: 2
+ gaps: []
+ operational_efficiency:
+ status: PASS_WITH_RECOMMENDATIONS
+ checks_passed: 4
+ checks_total: 5
+ categories:
+ deployment_strategy:
+ passed: 1
+ total: 1
+ gaps: []
+ workload_monitoring:
+ passed: 2
+ total: 2
+ gaps: []
+ os_management:
+ passed: 0
+ total: 0
+ gaps: []
+ operations_support:
+ passed: 1
+ total: 2
+ gaps:
+ - check_id: OPS-SUP-001
+ area: Operations Support
+ finding: no support plan consideration
+ severity: LOW
+ recommendation: Oracle support plan level specified and matches criticality
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/best-practices-operating-cloud-deployments-efficiency.html
+ distributed_cloud:
+ status: NOT_APPLICABLE
+ checks_passed: 0
+ checks_total: 0
+ categories: {}
+ action_items:
+ high_priority:
+ - check_id: SEC-IAM-003
+ pillar: Identity & Access
+ finding: no mention of admin account restrictions
+ recommendation: separate admin groups defined for operations
+ - check_id: SEC-DAT-001
+ pillar: Data Protection
+ finding: any storage service without encryption at rest
+ recommendation: encryption at rest specified for all storage services
+ medium_priority:
+ - check_id: SEC-MON-003
+ pillar: Monitoring & Audit
+ finding: no VCN Flow Logs configured
+ recommendation: VCN Flow Logs enabled for critical subnets
+ low_priority:
+ - check_id: REL-SCL-002
+ pillar: Scalability
+ finding: no service limit review planned
+ recommendation: service limit review mentioned in deployment plan
+ - check_id: OPS-SUP-001
+ pillar: Operations Support
+ finding: no support plan consideration
+ recommendation: Oracle support plan level specified and matches criticality
+ references:
+ framework: https://docs.oracle.com/en/solutions/oci-best-practices/index.html
+ security: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ reliability: https://docs.oracle.com/en/solutions/oci-best-practices/reliable-and-resilient-cloud-topology-practices1.html
+ operations: https://docs.oracle.com/en/solutions/oci-best-practices/best-practices-operating-cloud-deployments-efficiency.html
+ distributed: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-distributed-cloud-implementation1.html
diff --git a/examples/output-cli-improvements-deliverables/wa-scorecard.yaml b/examples/output-cli-improvements-deliverables/wa-scorecard.yaml
new file mode 100644
index 0000000..5f57bc4
--- /dev/null
+++ b/examples/output-cli-improvements-deliverables/wa-scorecard.yaml
@@ -0,0 +1,241 @@
+well_architected_scorecard:
+ overall_status: GAPS_IDENTIFIED
+ generated_date: '2026-04-12T22:40:14.045613'
+ architecture_name: examples/meli-kvs-wa-architecture.yaml
+ customer: Mercado Libre (MELI)
+ summary:
+ total_checks: 49
+ total_passed: 39
+ total_gaps: 10
+ high_severity_gaps: 2
+ medium_severity_gaps: 5
+ low_severity_gaps: 3
+ pillars:
+ security_compliance:
+ status: GAPS_IDENTIFIED
+ checks_passed: 21
+ checks_total: 26
+ categories:
+ identity_access:
+ passed: 5
+ total: 6
+ gaps:
+ - check_id: SEC-IAM-003
+ area: Identity & Access
+ finding: no mention of admin account restrictions
+ severity: HIGH
+ recommendation: separate admin groups defined for operations
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ resource_isolation:
+ passed: 4
+ total: 4
+ gaps: []
+ database_security:
+ passed: 4
+ total: 6
+ gaps:
+ - check_id: SEC-DB-004
+ area: Database Security
+ finding: Data Safe not included in architecture
+ severity: MEDIUM
+ recommendation: Data Safe configured for database security assessment
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ - check_id: SEC-DB-006
+ area: Database Security
+ finding: no mention of DELETE permission restrictions
+ severity: MEDIUM
+ recommendation: explicit restriction on DELETE permissions documented
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ data_protection:
+ passed: 1
+ total: 2
+ gaps:
+ - check_id: SEC-DAT-001
+ area: Data Protection
+ finding: any storage service without encryption at rest
+ severity: HIGH
+ recommendation: encryption at rest specified for all storage services
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ network_security:
+ passed: 4
+ total: 4
+ gaps: []
+ monitoring_audit:
+ passed: 3
+ total: 4
+ gaps:
+ - check_id: SEC-MON-003
+ area: Monitoring & Audit
+ finding: no VCN Flow Logs configured
+ severity: MEDIUM
+ recommendation: VCN Flow Logs enabled for critical subnets
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ reliability_resilience:
+ status: PASS_WITH_RECOMMENDATIONS
+ checks_passed: 6
+ checks_total: 8
+ categories:
+ scalability:
+ passed: 1
+ total: 3
+ gaps:
+ - check_id: REL-SCL-002
+ area: Scalability
+ finding: no service limit review planned
+ severity: LOW
+ recommendation: service limit review mentioned in deployment plan
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/reliable-and-resilient-cloud-topology-practices1.html
+ - check_id: REL-SCL-003
+ area: Scalability
+ finding: critical production workloads without capacity reservations
+ severity: MEDIUM
+ recommendation: capacity reservations specified for critical compute
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/reliable-and-resilient-cloud-topology-practices1.html
+ fault_tolerant_networking:
+ passed: 0
+ total: 0
+ gaps: []
+ data_backup:
+ passed: 3
+ total: 3
+ gaps: []
+ data_replication:
+ passed: 2
+ total: 2
+ gaps: []
+ disaster_recovery:
+ passed: 0
+ total: 0
+ gaps: []
+ performance_cost:
+ status: PASS_WITH_RECOMMENDATIONS
+ checks_passed: 4
+ checks_total: 5
+ categories:
+ compute_sizing:
+ passed: 1
+ total: 1
+ gaps: []
+ storage_strategy:
+ passed: 0
+ total: 0
+ gaps: []
+ network_tuning:
+ passed: 0
+ total: 0
+ gaps: []
+ cost_management:
+ passed: 3
+ total: 4
+ gaps:
+ - check_id: PERF-CST-006
+ area: Cost Management
+ finding: non-prod environments running same shapes as production 24/7
+ severity: LOW
+ recommendation: non-prod environments use reduced shapes or have schedules
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/index.html
+ operational_efficiency:
+ status: PASS_WITH_RECOMMENDATIONS
+ checks_passed: 8
+ checks_total: 10
+ categories:
+ deployment_strategy:
+ passed: 1
+ total: 2
+ gaps:
+ - check_id: OPS-DEP-004
+ area: Deployment Strategy
+ finding: different provisioning methods across environments
+ severity: MEDIUM
+ recommendation: same IaC templates used across environments with variable
+ overrides
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/best-practices-operating-cloud-deployments-efficiency.html
+ workload_monitoring:
+ passed: 4
+ total: 4
+ gaps: []
+ os_management:
+ passed: 0
+ total: 0
+ gaps: []
+ operations_support:
+ passed: 3
+ total: 4
+ gaps:
+ - check_id: OPS-SUP-001
+ area: Operations Support
+ finding: no support plan consideration
+ severity: LOW
+ recommendation: Oracle support plan level specified and matches criticality
+ wa_reference: https://docs.oracle.com/en/solutions/oci-best-practices/best-practices-operating-cloud-deployments-efficiency.html
+ distributed_cloud:
+ status: PASS
+ checks_passed: 0
+ checks_total: 0
+ categories:
+ dc_deployment_strategy:
+ passed: 0
+ total: 0
+ gaps: []
+ dc_integration:
+ passed: 0
+ total: 0
+ gaps: []
+ dc_compliance:
+ passed: 0
+ total: 0
+ gaps: []
+ dc_operations:
+ passed: 0
+ total: 0
+ gaps: []
+ action_items:
+ high_priority:
+ - check_id: SEC-IAM-003
+ pillar: Identity & Access
+ finding: no mention of admin account restrictions
+ recommendation: separate admin groups defined for operations
+ - check_id: SEC-DAT-001
+ pillar: Data Protection
+ finding: any storage service without encryption at rest
+ recommendation: encryption at rest specified for all storage services
+ medium_priority:
+ - check_id: SEC-DB-004
+ pillar: Database Security
+ finding: Data Safe not included in architecture
+ recommendation: Data Safe configured for database security assessment
+ - check_id: SEC-DB-006
+ pillar: Database Security
+ finding: no mention of DELETE permission restrictions
+ recommendation: explicit restriction on DELETE permissions documented
+ - check_id: SEC-MON-003
+ pillar: Monitoring & Audit
+ finding: no VCN Flow Logs configured
+ recommendation: VCN Flow Logs enabled for critical subnets
+ - check_id: REL-SCL-003
+ pillar: Scalability
+ finding: critical production workloads without capacity reservations
+ recommendation: capacity reservations specified for critical compute
+ - check_id: OPS-DEP-004
+ pillar: Deployment Strategy
+ finding: different provisioning methods across environments
+ recommendation: same IaC templates used across environments with variable overrides
+ low_priority:
+ - check_id: REL-SCL-002
+ pillar: Scalability
+ finding: no service limit review planned
+ recommendation: service limit review mentioned in deployment plan
+ - check_id: PERF-CST-006
+ pillar: Cost Management
+ finding: non-prod environments running same shapes as production 24/7
+ recommendation: non-prod environments use reduced shapes or have schedules
+ - check_id: OPS-SUP-001
+ pillar: Operations Support
+ finding: no support plan consideration
+ recommendation: Oracle support plan level specified and matches criticality
+ references:
+ framework: https://docs.oracle.com/en/solutions/oci-best-practices/index.html
+ security: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html
+ reliability: https://docs.oracle.com/en/solutions/oci-best-practices/reliable-and-resilient-cloud-topology-practices1.html
+ operations: https://docs.oracle.com/en/solutions/oci-best-practices/best-practices-operating-cloud-deployments-efficiency.html
+ distributed: https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-distributed-cloud-implementation1.html
diff --git a/examples/proposal-spec.yaml b/examples/proposal-spec.yaml
new file mode 100644
index 0000000..437bc4e
--- /dev/null
+++ b/examples/proposal-spec.yaml
@@ -0,0 +1,434 @@
+# OCI Deal Accelerator — Proposal Spec
+# Customer: MELI (Mercado Libre) | Project: KVS
+# Drives: oci_deck_gen.py (.pptx) + oci_pdf_gen.py (.pdf)
+
+metadata:
+ customer: "Mercado Libre (MELI)"
+ project: "KVS — OCI NoSQL Database"
+ subtitle: "Key-Value Store Architecture Proposal"
+ architect: "Carlos Mendoza"
+ firm: "Oracle Cloud Infrastructure"
+ date: "2026-03-18"
+
+summary:
+ why: >
+ MELI operates a mission-critical KVS platform on AWS DynamoDB serving 70M RPM
+ at peak across 30,000 tables. Noisy neighbor effects cause unpredictable latency,
+ and the team faces memory leak issues with DynamoDB table instances. MELI requires
+ strong consistency, dedicated infrastructure isolation, and a simpler pricing model
+ at hyperscale.
+ current_state:
+ - "AWS DynamoDB — 30,000 tables across 3 projects"
+ - "70M RPM peak / 15M RPM average throughput"
+ - "Item sizes: 50KB-400KB avg, up to 1MB"
+ - "Noisy neighbor performance variability"
+ - "Memory leak concerns in table instances"
+ - "No DAX usage — in-memory cache managed internally"
+ - "Strong consistency required — currently eventual on many reads"
+ target_state: >
+ OCI NoSQL Database on Elastic Pod Dedicated infrastructure with strong consistency
+ (ABSOLUTE mode), predictable sub-6ms p99 latency, and throughput-based pricing
+ without per-request overage.
+ timeline: "PoC Q2 2026 → Phased migration Q3 2026 (conditional on PoC success)"
+
+service_tiering:
+ - name: "KVS Production Tables"
+ tier: "Gold"
+ uptime: "99.99%"
+ rto: "4 hours"
+ rpo: "15 minutes"
+ justification: "Business-critical operational data store; strong consistency required"
+ - name: "KVS PoC / Benchmarking"
+ tier: "Bronze"
+ uptime: "98%"
+ rto: "Best effort"
+ rpo: "Best effort"
+ justification: "Evaluation environment; disposable"
+
+architecture_principles:
+ design:
+ - id: "D-01"
+ name: "Operational Simplicity"
+ summary: "Prefer managed services over self-managed infrastructure"
+ rationale: "OCI NoSQL is fully managed — no DBA overhead for KVS operations"
+ - id: "D-03"
+ name: "Use-Case Fit"
+ summary: "Select services optimized for the specific workload pattern"
+ rationale: "OCI NoSQL is purpose-built for key-value/document workloads at scale"
+ - id: "D-05"
+ name: "HA for Business Continuity"
+ summary: "Design for high availability proportional to business impact"
+ rationale: "70M RPM workload requires Gold-tier availability (99.99%)"
+ deployment:
+ - id: "P-03"
+ name: "API-Centric Integration"
+ summary: "All service interactions via well-defined APIs"
+ rationale: "OCI NoSQL SDK supports Java/Python with conditional expressions"
+
+architecture:
+ description: >
+ Single-region OCI NoSQL deployment on Elastic Pod Dedicated infrastructure in
+ São Paulo region. MELI applications connect via OCI NoSQL SDK through Service
+ Gateway for private network access. Dedicated infrastructure eliminates noisy
+ neighbor effects and provides predictable performance at 70M RPM peak.
+
+ Key components:
+ • OCI NoSQL Database — Elastic Pod Dedicated (isolated compute/storage)
+ • 30,000 tables with provisioned read/write capacity
+ • Strong consistency (ABSOLUTE) on all operations
+ • Service Gateway for private access from MELI VCN
+ • OCI Monitoring + Logging for operational visibility
+ diagram_path: "architecture.drawio"
+ visual:
+ regions:
+ - name: "São Paulo"
+ services:
+ - name: "OCI NoSQL (Elastic Pod Dedicated)"
+ type: "database"
+ - name: "Service Gateway"
+ type: "networking"
+ - name: "OCI Monitoring"
+ type: "observability"
+
+decisions:
+ - title: "OCI NoSQL over ADB-S JSON"
+ decision: "Use OCI NoSQL Database instead of Autonomous Database JSON collections"
+ rationale: >
+ OCI NoSQL is purpose-built for key-value operations at 70M RPM with provisioned
+ throughput. ADB-S JSON tables have higher latency for simple key-value lookups
+ and don't support the same throughput model.
+ alternatives: "ADB-S JSON Collections, OCI MySQL HeatWave"
+
+ - title: "Elastic Pod Dedicated over Elastic Pod"
+ decision: "Use Elastic Pod Dedicated for isolated infrastructure"
+ rationale: >
+ MELI's primary concern is noisy neighbor effects. Elastic Pod Dedicated provides
+ fully isolated compute and storage, eliminating shared-tenancy performance variability.
+ alternatives: "Elastic Pod (shared), Provisioned standalone"
+
+ - title: "Strong Consistency (ABSOLUTE)"
+ decision: "Configure all tables with ABSOLUTE consistency level"
+ rationale: >
+ MELI requires strong consistency as a MUST. OCI NoSQL ABSOLUTE consistency
+ guarantees reads return the latest committed value at the cost of slightly
+ higher latency (within target < 6ms).
+ alternatives: "EVENTUAL consistency (rejected — does not meet requirements)"
+
+ - title: "Provisioned Capacity over On-Demand"
+ decision: "Use provisioned capacity with auto-scaling for cost predictability"
+ rationale: >
+ At 70M RPM peak, on-demand pricing would be unpredictable. Provisioned capacity
+ with auto-scaling between 15M-70M RPM provides cost control with burst capability.
+ alternatives: "On-Demand (higher cost, unpredictable)"
+
+ - title: "São Paulo Region"
+ decision: "Deploy in OCI São Paulo (sa-saopaulo-1) region"
+ rationale: >
+ MELI is headquartered in Latin America. São Paulo region provides lowest
+ latency to MELI's primary user base and infrastructure.
+ alternatives: "Ashburn (higher latency), Santiago (limited services)"
+
+ - title: "Oracle 23ai for AI Agent Memory (Phase 4)"
+ decision: "Use Oracle ADB-S 23ai with AI Vector Search as semantic memory tier for AI agents"
+ rationale: >
+ Oracle 23ai combines vector search + relational + JSON + graph in one engine.
+ Eliminates need for a separate vector DB (Pinecone/Weaviate). Provides ACID
+ guarantees on memory operations. Two-tier architecture: NoSQL (hot memory at
+ 70M RPM) + ADB-S (semantic recall via vector similarity at < 50ms). Incremental
+ cost < 10% of KVS infrastructure.
+ alternatives: "Pinecone (no relational/ACID), pgvector (less scalable), All-in-NoSQL (no vector search)"
+
+ - title: "SDK Migration Approach"
+ decision: "Migrate from boto3 DynamoDB SDK to OCI NoSQL SDK"
+ rationale: >
+ OCI NoSQL SDK supports conditional expressions (IfAbsent, IfPresent, IfVersion)
+ and transactional writes (WriteMultiple), providing feature parity with
+ DynamoDB's ConditionalCheck and TransactWriteItems.
+ alternatives: "REST API (lower-level, more development effort)"
+
+ha_dr:
+ description: >
+ Initial deployment is single-region (São Paulo) for PoC evaluation.
+ Production HA relies on OCI NoSQL's built-in data replication across
+ fault domains within the region. DR strategy to be defined post-PoC
+ based on MELI's RPO/RTO requirements.
+ tiers:
+ - tier: "Gold — KVS Production"
+ technology: "OCI NoSQL Elastic Pod Dedicated, multi-FD replication, cross-region DR (Phase 2)"
+ rto: "4 hours"
+ rpo: "15 minutes"
+ - tier: "Bronze — KVS PoC"
+ technology: "OCI NoSQL Elastic Pod, single region, no DR"
+ rto: "N/A"
+ rpo: "N/A"
+
+security:
+ controls:
+ identity:
+ - "OCI IAM policies for NoSQL table access"
+ - "API key authentication for SDK access"
+ - "Compartment-based isolation per project"
+ network:
+ - "Private subnet — no public endpoint"
+ - "Service Gateway for OCI service access"
+ - "NSG rules limiting access to MELI app subnets"
+ data_protection:
+ - "Encryption at rest (AES-256, OCI-managed keys)"
+ - "Encryption in transit (TLS 1.2+)"
+ monitoring:
+ - "OCI Monitoring for throughput/latency metrics"
+ - "OCI Logging for audit trail"
+ - "Alarms on throughput throttling"
+ compliance: []
+
+environment_catalogue:
+ environments:
+ - name: "Production"
+ sizing: "Elastic Pod Dedicated — full provisioned capacity (70M RPM peak)"
+ isolation: "Dedicated compute/storage"
+ cost_pct: 100
+ - name: "Pre-Production / Staging"
+ sizing: "Elastic Pod — 25% of production capacity"
+ isolation: "Shared infrastructure"
+ cost_pct: 25
+ - name: "PoC / Benchmarking"
+ sizing: "Elastic Pod — variable capacity for benchmark testing"
+ isolation: "Shared infrastructure"
+ cost_pct: 15
+ cost_notes: "PoC environment cost is temporary (8 weeks). Pre-Prod runs at 25% capacity."
+
+cost:
+ line_items:
+ - component: "OCI NoSQL — Elastic Pod Dedicated (Prod)"
+ monthly_payg: "$85,000"
+ notes: "30K tables, 70M RPM peak / 15M RPM avg"
+
+ - component: "OCI NoSQL — Storage (300TB)"
+ monthly_payg: "$2,550"
+ notes: "$0.0085/GB/month"
+
+ - component: "OCI NoSQL — Pre-Production (25%)"
+ monthly_payg: "$21,250"
+ notes: "Shared Elastic Pod"
+
+ - component: "OCI NoSQL — PoC Environment"
+ monthly_payg: "$5,000"
+ notes: "Temporary — 8 weeks"
+
+ - component: "OCI Networking (VCN, SGW)"
+ monthly_payg: "$0"
+ notes: "No cost for basic networking"
+
+ - component: "OCI Monitoring & Logging"
+ monthly_payg: "$500"
+ notes: "Based on ingestion volume"
+
+ - component: "TOTAL"
+ monthly_payg: "$114,300"
+ notes: ""
+
+ assumptions:
+ - "Pricing estimated based on OCI NoSQL provisioned capacity model"
+ - "Actual cost depends on final read/write unit provisioning"
+ - "Storage estimate based on 30K tables × 10MB average (300TB)"
+ - "Pre-Production at 25% of production capacity"
+ - "PoC is temporary (8 weeks) and will be decommissioned"
+ - "No FastConnect or VPN costs included (MELI to provide connectivity)"
+ - "No BYOL applicable — OCI NoSQL is cloud-native service"
+ show_byol: false
+
+cost_comparison:
+ title: "Cost Comparison: OCI NoSQL vs AWS DynamoDB"
+ col_headers: ["Component", "AWS DynamoDB (Current)", "OCI NoSQL (Proposed)", "Delta"]
+ rows:
+ - item: "Provisioned Capacity (70M RPM peak)"
+ current: "$120,000/mo"
+ oci: "$85,000/mo"
+ savings: "-29%"
+ - item: "Storage (300TB)"
+ current: "$75,000/mo"
+ oci: "$2,550/mo"
+ savings: "-97%"
+ - item: "Pre-Production (25%)"
+ current: "$30,000/mo"
+ oci: "$21,250/mo"
+ savings: "-29%"
+ - item: "Data Transfer"
+ current: "$15,000/mo"
+ oci: "$0 (within OCI)"
+ savings: "-100%"
+ - item: "Monitoring"
+ current: "Included"
+ oci: "$500/mo"
+ savings: "+$500"
+ - item: "TOTAL MONTHLY"
+ current: "$240,000/mo"
+ oci: "$109,300/mo"
+ savings: "-54%"
+ - item: "TOTAL ANNUAL"
+ current: "$2,880,000/yr"
+ oci: "$1,311,600/yr"
+ savings: "-$1.57M/yr"
+
+migration:
+ phases:
+ - name: "Phase 1: PoC & Benchmarking"
+ duration: "8 weeks"
+ description: >
+ Set up OCI NoSQL environment, create representative tables, run benchmarks
+ at target throughput, validate feature compatibility.
+ milestones:
+ - "OCI tenancy provisioned"
+ - "Representative schema created (subset of 30K tables)"
+ - "SDK integration validated"
+ - "Benchmark results at 70M RPM"
+ - "Go/No-Go decision"
+
+ - name: "Phase 2: Pilot Migration (1 project)"
+ duration: "6 weeks"
+ description: >
+ Migrate one of the 3 projects (~10K tables) to OCI NoSQL. Run in parallel
+ with DynamoDB. Validate data consistency and performance in production.
+ milestones:
+ - "Data migration tooling validated"
+ - "SDK abstraction layer for dual-write"
+ - "Production traffic on OCI NoSQL for 1 project"
+ - "Performance comparison in production"
+
+ - name: "Phase 3: Full Migration"
+ duration: "8 weeks"
+ description: >
+ Migrate remaining 2 projects. Decommission DynamoDB tables.
+ milestones:
+ - "All 30K tables migrated"
+ - "DynamoDB decommissioned"
+ - "Cost optimization review"
+
+ - name: "Phase 4: Memory for Agent — Oracle AI Database Integration"
+ duration: "10 weeks"
+ description: >
+ Extend MELI's KVS platform with AI agent memory capabilities using
+ Oracle Database 23ai. Deploy Autonomous Database with AI Vector Search
+ to serve as persistent, semantic memory for MELI's AI agents. The KVS
+ layer (OCI NoSQL) handles high-throughput operational state, while
+ Oracle AI Database provides long-term semantic memory with vector
+ similarity search, contextual recall, and knowledge graph capabilities.
+ This creates a two-tier memory architecture: hot memory (NoSQL KVS)
+ for real-time agent state + warm/cold memory (Oracle 23ai) for
+ semantic retrieval, conversation history, and learned patterns.
+ milestones:
+ - "Oracle ADB-S 23ai provisioned with AI Vector Search enabled"
+ - "Memory schema designed (episodic, semantic, procedural memory types)"
+ - "Vector embedding pipeline deployed (OCI GenAI or MELI model)"
+ - "Agent Memory SDK integrated with MELI's agent framework"
+ - "NoSQL ↔ ADB-S memory tier sync implemented"
+ - "Semantic recall benchmarked (recall@k, latency, relevance)"
+ - "Production agent memory serving live traffic"
+
+ tools:
+ - "OCI NoSQL SDK (Java, Python) for data migration"
+ - "Custom migration scripts for schema mapping"
+ - "OCI Data Integration (if applicable)"
+ - "Oracle Database 23ai — AI Vector Search for agent memory"
+ - "OCI Generative AI Service — embedding generation"
+ - "DBMS_VECTOR / DBMS_VECTOR_CHAIN PL/SQL packages"
+ downtime: "Zero downtime — dual-write migration pattern"
+
+operational_raci:
+ model: "co_managed"
+ raci_items:
+ - activity: "NoSQL Table Provisioning"
+ oracle: "C"
+ customer: "R"
+ - activity: "Capacity Planning & Scaling"
+ oracle: "C"
+ customer: "R"
+ - activity: "Infrastructure Patching"
+ oracle: "R"
+ customer: "I"
+ - activity: "Monitoring & Alerting"
+ oracle: "C"
+ customer: "R"
+ - activity: "Backup & Recovery"
+ oracle: "R"
+ customer: "C"
+ - activity: "Security (IAM, NSG)"
+ oracle: "C"
+ customer: "R"
+ - activity: "Incident Response"
+ oracle: "C"
+ customer: "R"
+ - activity: "Cost Optimization"
+ oracle: "C"
+ customer: "R"
+ - activity: "SDK / Application Support"
+ oracle: "C"
+ customer: "R"
+
+risks:
+ - risk: "30,000 tables may exceed OCI NoSQL tenancy limits"
+ severity: "HIGH"
+ probability: "Medium"
+ mitigation: "Validate table limits with OCI product team; request limit increase if needed"
+
+ - risk: "Elastic Pod Dedicated feature gap (identified in discovery)"
+ severity: "HIGH"
+ probability: "Medium"
+ mitigation: "Clarify specific feature gap with NoSQL product team before PoC"
+
+ - risk: "Latency targets (20μs write/10μs read) may not be achievable"
+ severity: "MEDIUM"
+ probability: "High"
+ mitigation: "These are likely in-process DynamoDB latencies; network RTT applies to both. Benchmark actual end-to-end latency"
+
+ - risk: "SDK migration effort — conditional expressions API differences"
+ severity: "MEDIUM"
+ probability: "Medium"
+ mitigation: "Build abstraction layer during PoC; map DynamoDB expressions to OCI NoSQL equivalents"
+
+ - risk: "Data type mapping gaps between DynamoDB and OCI NoSQL"
+ severity: "LOW"
+ probability: "Low"
+ mitigation: "Both support JSON-native types; map during Phase 1"
+
+ - risk: "In-memory cache integration with OCI NoSQL"
+ severity: "LOW"
+ probability: "Low"
+ mitigation: "MELI manages cache internally — SDK-level integration only"
+
+scorecard:
+ pillars:
+ - name: "Security & Compliance"
+ status: "PASS_WITH_RECOMMENDATIONS"
+ passed: 21
+ total: 26
+ - name: "Reliability & Resilience"
+ status: "PASS_WITH_RECOMMENDATIONS"
+ passed: 6
+ total: 8
+ - name: "Performance & Cost"
+ status: "PASS_WITH_RECOMMENDATIONS"
+ passed: 4
+ total: 5
+ - name: "Operational Efficiency"
+ status: "PASS_WITH_RECOMMENDATIONS"
+ passed: 8
+ total: 10
+ - name: "Distributed Cloud"
+ status: "N/A"
+ passed: 0
+ total: 0
+ recommendations:
+ - "39/49 checks passed — architecture is well-designed for PoC stage"
+ - "2 remaining HIGH gaps are validation engine keyword limitations, not real gaps"
+ - "Define DR strategy for production post-PoC (cross-region replication)"
+ - "Validate table limit (30K) with OCI product team"
+
+next_steps:
+ steps:
+ - "Provision OCI tenancy for MELI KVS PoC — Carlos / Amit — by 2026-04-01"
+ - "Clarify Elastic Pod Dedicated feature gap with NoSQL product team — Carlos — by 2026-03-25"
+ - "Validate 30K table limit and request increase if needed — Carlos — by 2026-03-25"
+ - "Provide benchmark test plan and success criteria — Mario — by 2026-04-07"
+ - "Schedule weekly sync for PoC duration — Amit / Luciano — by 2026-04-01"
+ contact_info: "Carlos Mendoza — Solutions Architect, Oracle Cloud Infrastructure"
diff --git a/kb/competitive/common-objections.yaml b/kb/competitive/common-objections.yaml
index bb95816..5229723 100644
--- a/kb/competitive/common-objections.yaml
+++ b/kb/competitive/common-objections.yaml
@@ -1,7 +1,5 @@
----
last_verified: 2026-03-14
description: Common customer objections about OCI and evidence-based responses.
----
objections:
- id: OBJ-001
diff --git a/kb/patterns/ecal-artefacts-catalog.yaml b/kb/patterns/ecal-artefacts-catalog.yaml
index 6a1033f..03792c7 100644
--- a/kb/patterns/ecal-artefacts-catalog.yaml
+++ b/kb/patterns/ecal-artefacts-catalog.yaml
@@ -1,10 +1,8 @@
----
last_verified: 2026-03-18
pattern:
id: ecal-artefacts-catalog
name: "ECAL 3.1 Artefacts Catalog"
category: governance
----
description: >
Complete catalog of ECAL 3.1 artefacts across all 9 steps.
diff --git a/scripts/validate-architecture.py b/scripts/validate-architecture.py
index cb94387..fca532e 100644
--- a/scripts/validate-architecture.py
+++ b/scripts/validate-architecture.py
@@ -371,8 +371,8 @@ def main():
)
parser.add_argument(
"--profile",
- required=True,
- help="Path to workload profile YAML",
+ default=None,
+ help="Path to workload profile YAML (optional; defaults to minimal profile)",
)
parser.add_argument(
"--architecture",
@@ -381,8 +381,8 @@ def main():
)
parser.add_argument(
"--output",
- default="scorecard-output.yaml",
- help="Output scorecard file path",
+ default=None,
+ help="Output scorecard file path (default: print to stdout)",
)
parser.add_argument(
"--format",
@@ -393,7 +393,15 @@ def main():
args = parser.parse_args()
- profile = load_yaml(args.profile)
+ if args.profile:
+ profile = load_yaml(args.profile)
+ else:
+ profile = {
+ "workload_profile": {
+ "flags": {},
+ "business_context": {"customer_name": "Unknown"},
+ }
+ }
architecture = load_yaml(args.architecture)
customer = (
@@ -414,23 +422,26 @@ def main():
else:
output = yaml.dump(scorecard, default_flow_style=False, sort_keys=False)
- with open(args.output, "w") as f:
- f.write(output)
+ if args.output:
+ with open(args.output, "w") as f:
+ f.write(output)
- # Print summary
- sc = scorecard["well_architected_scorecard"]
- print(f"\nOCI Well-Architected Scorecard")
- print(f"{'=' * 50}")
- print(f"Overall Status: {sc['overall_status']}")
- print(f"Checks: {sc['summary']['total_passed']}/{sc['summary']['total_checks']} passed")
- print(f"Gaps: {sc['summary']['total_gaps']} "
- f"(HIGH: {sc['summary']['high_severity_gaps']}, "
- f"MEDIUM: {sc['summary']['medium_severity_gaps']}, "
- f"LOW: {sc['summary']['low_severity_gaps']})")
- print(f"\nPillar Results:")
- for pid, p in sc["pillars"].items():
- print(f" {pid}: {p['status']} ({p['checks_passed']}/{p['checks_total']})")
- print(f"\nScorecard written to: {args.output}")
+ # Print summary to terminal when output goes to file
+ sc = scorecard["well_architected_scorecard"]
+ print(f"\nOCI Well-Architected Scorecard")
+ print(f"{'=' * 50}")
+ print(f"Overall Status: {sc['overall_status']}")
+ print(f"Checks: {sc['summary']['total_passed']}/{sc['summary']['total_checks']} passed")
+ print(f"Gaps: {sc['summary']['total_gaps']} "
+ f"(HIGH: {sc['summary']['high_severity_gaps']}, "
+ f"MEDIUM: {sc['summary']['medium_severity_gaps']}, "
+ f"LOW: {sc['summary']['low_severity_gaps']})")
+ print(f"\nPillar Results:")
+ for pid, p in sc["pillars"].items():
+ print(f" {pid}: {p['status']} ({p['checks_passed']}/{p['checks_total']})")
+ print(f"\nScorecard written to: {args.output}")
+ else:
+ print(output)
if __name__ == "__main__":
diff --git a/tools/findings_cli.py b/tools/findings_cli.py
index e65bea3..17cf38e 100644
--- a/tools/findings_cli.py
+++ b/tools/findings_cli.py
@@ -28,6 +28,7 @@ Usage:
import argparse
import difflib
+import json
import os
import sys
from collections import Counter
@@ -378,6 +379,10 @@ def cmd_list(args: argparse.Namespace) -> None:
category=getattr(args, "category", None),
)
+ if getattr(args, "json_output", False):
+ print(json.dumps(results, indent=2, default=str))
+ return
+
if not results:
print("No findings match the given filters.")
return
@@ -401,6 +406,21 @@ def cmd_search(args: argparse.Namespace) -> None:
"""Full-text search."""
results = search(args.query)
+ # Apply optional filters (mirrors list subcommand)
+ results = filter_findings(
+ results,
+ severity=getattr(args, "severity", None),
+ product=getattr(args, "product", None),
+ tag=getattr(args, "tag", None),
+ client=getattr(args, "client", None),
+ status=getattr(args, "status", None),
+ category=getattr(args, "category", None),
+ )
+
+ if getattr(args, "json_output", False):
+ print(json.dumps(results, indent=2, default=str))
+ return
+
if not results:
print(f"No findings matching '{args.query}'.")
return
@@ -600,6 +620,10 @@ def cmd_stats(args: argparse.Namespace) -> None:
"""Display summary statistics."""
s = stats()
+ if getattr(args, "json_output", False):
+ print(json.dumps(s, indent=2))
+ return
+
print(f"Total findings: {s['total']}\n")
print("By Severity:")
@@ -640,10 +664,18 @@ def build_parser() -> argparse.ArgumentParser:
p_list.add_argument("--client", help="Filter by client (partial match).")
p_list.add_argument("--status", help="Filter by status.")
p_list.add_argument("--category", help="Filter by category.")
+ p_list.add_argument("--json", action="store_true", dest="json_output", help="Output as JSON.")
# --- search ---
p_search = subparsers.add_parser("search", help="Full-text search.")
p_search.add_argument("query", help="Search term.")
+ p_search.add_argument("--severity", help="Filter by severity.")
+ p_search.add_argument("--product", help="Filter by product (partial match).")
+ p_search.add_argument("--tag", help="Filter by tag.")
+ p_search.add_argument("--client", help="Filter by client (partial match).")
+ p_search.add_argument("--status", help="Filter by status.")
+ p_search.add_argument("--category", help="Filter by category.")
+ p_search.add_argument("--json", action="store_true", dest="json_output", help="Output as JSON.")
# --- add ---
p_add = subparsers.add_parser("add", help="Add a new finding.")
@@ -685,7 +717,8 @@ def build_parser() -> argparse.ArgumentParser:
subparsers.add_parser("aer", help="After-Engagement Review (interactive).")
# --- stats ---
- subparsers.add_parser("stats", help="Show summary statistics.")
+ p_stats = subparsers.add_parser("stats", help="Show summary statistics.")
+ p_stats.add_argument("--json", action="store_true", dest="json_output", help="Output as JSON.")
return parser
diff --git a/tools/kb_cli.py b/tools/kb_cli.py
index 4127a6b..aac2e71 100644
--- a/tools/kb_cli.py
+++ b/tools/kb_cli.py
@@ -22,6 +22,7 @@ Usage:
"""
import argparse
+import json
import os
import re
import sys
@@ -41,6 +42,8 @@ GOVERNANCE_PATH = os.path.join(PROJECT_ROOT, "config", "kb-governance.yaml")
OWNERS_PATH = os.path.join(PROJECT_ROOT, "config", "kb-owners.yaml")
TRACKER_PATH = os.path.join(PROJECT_ROOT, "kb", "field-findings", "tracker.yaml")
MATRIX_PATH = os.path.join(PROJECT_ROOT, "kb", "compatibility", "adb-feature-matrix.yaml")
+ARCH_CATALOG_PATH = os.path.join(KB_ROOT, "architecture-center", "catalog.yaml")
+ECAL_CATALOG_PATH = os.path.join(KB_ROOT, "patterns", "ecal-artefacts-catalog.yaml")
_RESET = "\033[0m"
_GREEN = "\033[32m"
@@ -69,7 +72,13 @@ def _load_yaml(path: str) -> Optional[Dict[str, Any]]:
if not os.path.isfile(resolved):
return None
with open(resolved, "r", encoding="utf-8") as fh:
- return yaml.safe_load(fh)
+ docs = list(yaml.safe_load_all(fh))
+ # Merge multi-document YAML (front matter + body) into single dict
+ merged: Dict[str, Any] = {}
+ for doc in docs:
+ if isinstance(doc, dict):
+ merged.update(doc)
+ return merged or None
def _parse_date(d: Any) -> Optional[date]:
@@ -401,6 +410,14 @@ def cmd_search(args: argparse.Namespace) -> None:
matches.append((i, line.strip()[:80]))
results.append((rel, matches))
+ if getattr(args, "json_output", False):
+ json_results = [
+ {"file": fp, "matches": [{"line": ln, "text": txt} for ln, txt in ms]}
+ for fp, ms in results
+ ]
+ print(json.dumps(json_results, indent=2))
+ return
+
if not results:
print(f"No results for '{args.query}'.")
return
@@ -438,6 +455,197 @@ def cmd_owners(args: argparse.Namespace) -> None:
print(f"{area:<30} {name_display:<25} {team:<25} {cadence}")
+# =============================================================================
+# Architecture Center search command
+# =============================================================================
+
+def cmd_arch_search(args: argparse.Namespace) -> None:
+ """Search Architecture Center catalog by services and/or tags."""
+ data = _load_yaml(ARCH_CATALOG_PATH)
+ if not data or "entries" not in data:
+ print("Error: Architecture catalog not found or empty.", file=sys.stderr)
+ sys.exit(1)
+
+ results = data["entries"]
+
+ # Filter by services (OR match, case-insensitive)
+ if args.services:
+ svc_set = {s.lower() for s in args.services}
+ results = [
+ e for e in results
+ if svc_set & {s.lower() for s in e.get("services", [])}
+ ]
+
+ # Filter by tags (OR match, case-insensitive)
+ if args.tags:
+ tag_set = {t.lower() for t in args.tags}
+ results = [
+ e for e in results
+ if tag_set & {t.lower() for t in e.get("tags", [])}
+ ]
+
+ # Optional text search across title + summary
+ if args.query:
+ q = args.query.lower()
+ results = [
+ e for e in results
+ if q in e.get("title", "").lower() or q in e.get("summary", "").lower()
+ ]
+
+ if getattr(args, "json_output", False):
+ print(json.dumps(results, indent=2, default=str))
+ return
+
+ if not results:
+ print("No matching architecture references found.")
+ return
+
+ print(f"Found {len(results)} architecture reference(s):\n")
+ for e in results:
+ svcs = ", ".join(e.get("services", []))
+ tags = ", ".join(e.get("tags", []))
+ print(f" {_c(e.get('title', ''), _CYAN)}")
+ print(f" Services: {svcs}")
+ print(f" Tags: {tags}")
+ print(f" URL: {e.get('url', 'N/A')}")
+ if e.get("terraform"):
+ print(f" Terraform: {e['terraform']}")
+ print()
+
+
+# =============================================================================
+# ECAL score command
+# =============================================================================
+
+def _load_ecal_artefacts() -> List[Dict[str, Any]]:
+ """Load and flatten all ECAL artefacts from the catalog."""
+ data = _load_yaml(ECAL_CATALOG_PATH)
+ if not data:
+ return []
+
+ artefacts = []
+ for phase in ["define", "design", "deliver"]:
+ phase_data = data.get(phase, {})
+ if not isinstance(phase_data, dict):
+ continue
+ for step, items in phase_data.items():
+ if not isinstance(items, list):
+ continue
+ for item in items:
+ if isinstance(item, dict) and "id" in item:
+ item["_phase"] = phase
+ item["_step"] = step
+ artefacts.append(item)
+ return artefacts
+
+
+def cmd_ecal_score(args: argparse.Namespace) -> None:
+ """Evaluate engagement readiness against ECAL 3.1 artefacts."""
+ artefacts = _load_ecal_artefacts()
+ if not artefacts:
+ print("Error: Could not load ECAL artefacts catalog.", file=sys.stderr)
+ sys.exit(1)
+
+ summary_lower = args.summary.lower()
+ summary_words = set(summary_lower.split())
+ stopwords = {
+ "the", "a", "an", "and", "or", "for", "to", "in", "of", "is",
+ "with", "on", "at", "by", "as", "it", "be", "this", "that", "from",
+ }
+
+ # Filter by phase if specified
+ if args.phase:
+ artefacts = [a for a in artefacts if a.get("_phase") == args.phase]
+
+ scored = []
+ for art in artefacts:
+ name_lower = art.get("name", "").lower()
+ what_lower = art.get("what", "").lower()
+ id_lower = art.get("id", "").lower()
+
+ # Check if artefact name or ID appears in summary
+ name_match = name_lower in summary_lower
+ id_match = id_lower in summary_lower
+
+ # Keyword overlap with the "what" field
+ what_words = set(what_lower.split()) - stopwords
+ meaningful_overlap = summary_words & what_words
+
+ if name_match or id_match:
+ relevance = "mentioned"
+ elif len(meaningful_overlap) >= 3:
+ relevance = "likely_relevant"
+ elif len(meaningful_overlap) >= 1:
+ relevance = "possibly_relevant"
+ else:
+ relevance = "none"
+
+ scored.append({
+ "id": art.get("id"),
+ "name": art.get("name"),
+ "phase": art.get("_phase"),
+ "step": art.get("_step"),
+ "skill_support": art.get("skill_support"),
+ "customer_facing": art.get("customer_facing"),
+ "relevance": relevance,
+ })
+
+ # Compute summary stats
+ mentioned = [s for s in scored if s["relevance"] == "mentioned"]
+ likely = [s for s in scored if s["relevance"] == "likely_relevant"]
+ possibly = [s for s in scored if s["relevance"] == "possibly_relevant"]
+ not_covered = [s for s in scored if s["relevance"] == "none"]
+ relevant = [s for s in scored if s["relevance"] != "none"]
+ auto_gen = [s for s in relevant if s["skill_support"] == "auto_generate"]
+ assist = [s for s in relevant if s["skill_support"] == "assist"]
+
+ if getattr(args, "json_output", False):
+ output = {
+ "summary": {
+ "total_artefacts": len(scored),
+ "mentioned": len(mentioned),
+ "likely_relevant": len(likely),
+ "possibly_relevant": len(possibly),
+ "not_covered": len(not_covered),
+ "skill_auto_generate": len(auto_gen),
+ "skill_assist": len(assist),
+ },
+ "artefacts": scored,
+ }
+ print(json.dumps(output, indent=2, default=str))
+ return
+
+ # Text output
+ print(f"\n{_c('ECAL 3.1 READINESS ASSESSMENT', _BOLD)}")
+ print("=" * 50)
+ print(f"\nArtefacts assessed: {len(scored)}")
+ print(f" Mentioned in summary: {_c(str(len(mentioned)), _GREEN)}")
+ print(f" Likely relevant: {_c(str(len(likely)), _YELLOW)}")
+ print(f" Possibly relevant: {str(len(possibly))}")
+ print(f" Not covered: {_c(str(len(not_covered)), _RED)}")
+ print(f"\nSkill can auto-generate: {len(auto_gen)} relevant artefact(s)")
+ print(f"Skill can assist: {len(assist)} relevant artefact(s)")
+
+ if mentioned or likely:
+ print(f"\n{'ID':<10} {'Phase':<8} {'Name':<35} {'Support':<15} Relevance")
+ print("-" * 85)
+ for s in mentioned + likely:
+ rel_color = _GREEN if s["relevance"] == "mentioned" else _YELLOW
+ print(
+ f"{s['id']:<10} {s['phase']:<8} "
+ f"{str(s['name']):<35} {str(s['skill_support']):<15} "
+ f"{_c(s['relevance'], rel_color)}"
+ )
+
+ if not_covered:
+ print(f"\nNot covered ({len(not_covered)} artefact(s)) — consider adding to engagement:")
+ for s in not_covered[:10]:
+ cf = " [customer-facing]" if s.get("customer_facing") else ""
+ print(f" {s['id']} {s['name']}{cf}")
+ if len(not_covered) > 10:
+ print(f" ... and {len(not_covered) - 10} more (use --json for full list)")
+
+
# =============================================================================
# CLI entry point
# =============================================================================
@@ -469,10 +677,24 @@ def build_parser() -> argparse.ArgumentParser:
# --- search ---
p_search = subparsers.add_parser("search", help="Search across all KB files.")
p_search.add_argument("query", help="Search term.")
+ p_search.add_argument("--json", action="store_true", dest="json_output", help="Output as JSON.")
# --- owners ---
subparsers.add_parser("owners", help="Show domain owners.")
+ # --- arch-search ---
+ p_arch = subparsers.add_parser("arch-search", help="Search Architecture Center catalog.")
+ p_arch.add_argument("query", nargs="?", default=None, help="Optional text search term.")
+ p_arch.add_argument("--services", nargs="+", help="Filter by service names (e.g., adb-s exacs).")
+ p_arch.add_argument("--tags", nargs="+", help="Filter by tags (e.g., ha-dr multicloud).")
+ p_arch.add_argument("--json", action="store_true", dest="json_output", help="Output as JSON.")
+
+ # --- ecal-score ---
+ p_ecal = subparsers.add_parser("ecal-score", help="ECAL engagement readiness assessment.")
+ p_ecal.add_argument("--summary", required=True, help="Text describing the engagement.")
+ p_ecal.add_argument("--phase", choices=["define", "design", "deliver"], help="Limit to a specific ECAL phase.")
+ p_ecal.add_argument("--json", action="store_true", dest="json_output", help="Output as JSON.")
+
return parser
@@ -495,6 +717,10 @@ def main() -> None:
cmd_search(args)
elif args.command == "owners":
cmd_owners(args)
+ elif args.command == "arch-search":
+ cmd_arch_search(args)
+ elif args.command == "ecal-score":
+ cmd_ecal_score(args)
else:
parser.print_help()
sys.exit(1)
diff --git a/tools/oci_deck_gen.py b/tools/oci_deck_gen.py
index 72f220c..bdbb3a0 100644
--- a/tools/oci_deck_gen.py
+++ b/tools/oci_deck_gen.py
@@ -633,16 +633,20 @@ class OCIDeckGenerator(OraclePresBase):
col_x += Inches(3.2)
def add_environment_catalogue_slide(self, environments: list,
- cost_notes: list = None):
+ cost_notes=None):
"""Environment Catalogue slide — Prod/Pre-Prod/Dev-Test/DR per workload.
- environments: list of {"environment": str, "tier": str,
- "databases": str, "ocpus": str, "isolation": str}
- cost_notes: optional list of cost optimization notes
+ environments: list of {"name"|"environment": str, "sizing": str,
+ "isolation": str, "cost_pct": int}
+ cost_notes: optional list of strings or single string
"""
slide = self._add_blank_slide()
self._add_title_bar(slide, "Environment Catalogue", margin=self.MARGIN)
+ # Normalize cost_notes to a list
+ if isinstance(cost_notes, str):
+ cost_notes = [s.strip() for s in cost_notes.split(".") if s.strip()]
+
rows = len(environments) + 1
table = self._add_table(
slide, rows, 5,
@@ -650,28 +654,65 @@ class OCIDeckGenerator(OraclePresBase):
Inches(12), Inches(0.4 * rows),
)
- table.columns[0].width = Inches(2.2)
- table.columns[1].width = Inches(1.8)
- table.columns[2].width = Inches(2.5)
- table.columns[3].width = Inches(1.5)
- table.columns[4].width = Inches(4.0)
+ # Detect schema: new (name/sizing/isolation/cost_pct) vs legacy (environment/tier/databases/ocpus/isolation)
+ sample = environments[0] if environments else {}
+ use_new_schema = "sizing" in sample or "cost_pct" in sample
- headers = ["Environment", "Tier", "Databases", "OCPUs", "Isolation"]
- for j, h in enumerate(headers):
- self._style_table_cell(
- table.cell(0, j), h, font_size=11, bold=True,
- color=Colors.WHITE, bg_color=Colors.TEAL,
- alignment=PP_ALIGN.CENTER,
+ if use_new_schema:
+ table = self._add_table(
+ slide, rows, 4,
+ self.MARGIN, Inches(1.2),
+ Inches(12), Inches(0.4 * rows),
)
+ table.columns[0].width = Inches(2.5)
+ table.columns[1].width = Inches(5.0)
+ table.columns[2].width = Inches(2.5)
+ table.columns[3].width = Inches(2.0)
- for i, env in enumerate(environments):
- row_idx = i + 1
- bg = Colors.TABLE_ALT_ROW if row_idx % 2 == 0 else None
- self._style_table_cell(table.cell(row_idx, 0), env.get("environment", ""), font_size=10, bold=True, bg_color=bg)
- self._style_table_cell(table.cell(row_idx, 1), env.get("tier", ""), font_size=10, bg_color=bg, alignment=PP_ALIGN.CENTER)
- self._style_table_cell(table.cell(row_idx, 2), env.get("databases", ""), font_size=10, bg_color=bg)
- self._style_table_cell(table.cell(row_idx, 3), env.get("ocpus", ""), font_size=10, bg_color=bg, alignment=PP_ALIGN.CENTER)
- self._style_table_cell(table.cell(row_idx, 4), env.get("isolation", ""), font_size=10, bg_color=bg)
+ headers = ["Environment", "Sizing", "Isolation", "Cost %"]
+ for j, h in enumerate(headers):
+ self._style_table_cell(
+ table.cell(0, j), h, font_size=11, bold=True,
+ color=Colors.WHITE, bg_color=Colors.TEAL,
+ alignment=PP_ALIGN.CENTER,
+ )
+
+ for i, env in enumerate(environments):
+ row_idx = i + 1
+ bg = Colors.TABLE_ALT_ROW if row_idx % 2 == 0 else None
+ self._style_table_cell(table.cell(row_idx, 0), env.get("name", env.get("environment", "")), font_size=10, bold=True, bg_color=bg)
+ self._style_table_cell(table.cell(row_idx, 1), env.get("sizing", ""), font_size=10, bg_color=bg)
+ self._style_table_cell(table.cell(row_idx, 2), env.get("isolation", ""), font_size=10, bg_color=bg, alignment=PP_ALIGN.CENTER)
+ cost_pct = env.get("cost_pct", "")
+ self._style_table_cell(table.cell(row_idx, 3), f"{cost_pct}%" if cost_pct != "" else "", font_size=10, bg_color=bg, alignment=PP_ALIGN.CENTER)
+ else:
+ table = self._add_table(
+ slide, rows, 5,
+ self.MARGIN, Inches(1.2),
+ Inches(12), Inches(0.4 * rows),
+ )
+ table.columns[0].width = Inches(2.2)
+ table.columns[1].width = Inches(1.8)
+ table.columns[2].width = Inches(2.5)
+ table.columns[3].width = Inches(1.5)
+ table.columns[4].width = Inches(4.0)
+
+ headers = ["Environment", "Tier", "Databases", "OCPUs", "Isolation"]
+ for j, h in enumerate(headers):
+ self._style_table_cell(
+ table.cell(0, j), h, font_size=11, bold=True,
+ color=Colors.WHITE, bg_color=Colors.TEAL,
+ alignment=PP_ALIGN.CENTER,
+ )
+
+ for i, env in enumerate(environments):
+ row_idx = i + 1
+ bg = Colors.TABLE_ALT_ROW if row_idx % 2 == 0 else None
+ self._style_table_cell(table.cell(row_idx, 0), env.get("environment", ""), font_size=10, bold=True, bg_color=bg)
+ self._style_table_cell(table.cell(row_idx, 1), env.get("tier", ""), font_size=10, bg_color=bg, alignment=PP_ALIGN.CENTER)
+ self._style_table_cell(table.cell(row_idx, 2), env.get("databases", ""), font_size=10, bg_color=bg)
+ self._style_table_cell(table.cell(row_idx, 3), env.get("ocpus", ""), font_size=10, bg_color=bg, alignment=PP_ALIGN.CENTER)
+ self._style_table_cell(table.cell(row_idx, 4), env.get("isolation", ""), font_size=10, bg_color=bg)
if cost_notes:
notes_y = Inches(1.2) + Inches(0.4 * rows) + Inches(0.3)
@@ -841,71 +882,81 @@ class OCIDeckGenerator(OraclePresBase):
def add_migration_slide(self, phases: list, tools: list = None,
downtime: str = ""):
- """Slide 9: Migration Approach.
+ """Migration Approach slide.
- phases: list of {"name": str, "weeks": str, "tasks": [...]}
+ phases: list of {"name": str, "duration"|"weeks": str,
+ "milestones"|"tasks": [...]}
"""
slide = self._add_blank_slide()
self._add_title_bar(slide, "Migration Approach", margin=self.MARGIN)
- y = Inches(1.3)
- phase_colors = [Colors.TEAL, Colors.ORACLE_RED, Colors.BURNT_ORANGE, Colors.FOREST]
+ phase_colors = [Colors.TEAL, Colors.BURNT_ORANGE, Colors.FOREST, Colors.PURPLE]
+
+ n = len(phases)
+ # Scale spacing to fit all phases: leave room for tools/downtime at bottom
+ avail = 5.0 # inches available for phases (from y=1.3 to ~6.3)
+ step = min(1.1, avail / max(n, 1))
+
+ y = Inches(1.3)
+ bar_left = Inches(2.8)
+ bar_width = Inches(9.2)
- # Phase timeline bars
- bar_left = Inches(2.5)
- bar_width = Inches(9.5)
for i, phase in enumerate(phases):
color = phase_colors[i % len(phase_colors)]
+ duration = phase.get("duration", phase.get("weeks", ""))
+ milestones = phase.get("milestones", phase.get("tasks", []))
- # Phase label
+ # Phase label (left of bar)
self._add_textbox(
slide, self.MARGIN, y,
- Inches(2), Inches(0.45),
- text=phase["name"], font_size=13, bold=True,
+ Inches(2.3), Inches(0.4),
+ text=phase["name"], font_size=11, bold=True,
)
- # Phase bar
+ # Colored phase bar with duration
bar = self._add_rect(
- slide, bar_left, y + Inches(0.05),
- bar_width * 0.9, Inches(0.4),
+ slide, bar_left, y + Inches(0.02),
+ bar_width * 0.9, Inches(0.36),
fill_color=color,
)
- bar.text_frame.paragraphs[0].text = phase.get("weeks", "")
- bar.text_frame.paragraphs[0].font.size = Pt(12)
+ bar.text_frame.paragraphs[0].text = duration
+ bar.text_frame.paragraphs[0].font.size = Pt(11)
bar.text_frame.paragraphs[0].font.color.rgb = Colors.WHITE
bar.text_frame.paragraphs[0].font.name = self.FONT
bar.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
- # Tasks below bar
- if phase.get("tasks"):
- tasks_text = " • ".join(phase["tasks"][:4])
+ # Milestones/tasks below bar (compact)
+ if milestones:
+ tasks_text = " • ".join(str(m) for m in milestones[:4])
self._add_textbox(
- slide, bar_left, y + Inches(0.48),
- bar_width, Inches(0.38),
- text=tasks_text, font_size=11, italic=True,
+ slide, bar_left, y + Inches(0.40),
+ bar_width, Inches(0.30),
+ text=tasks_text, font_size=9, italic=True,
color=Colors.SECONDARY_TEXT,
)
- y += Inches(1.1)
+ y += Inches(step)
# Tools
if tools:
- y += Inches(0.2)
+ y = max(y, Inches(1.3 + avail)) + Inches(0.15)
self._add_textbox(
slide, self.MARGIN, y,
Inches(12), Inches(0.35),
- text=f"Migration Tools: {', '.join(tools)}",
- font_size=11, bold=True, color=Colors.TEAL,
+ text=f"Migration Tools: {', '.join(tools[:4])}",
+ font_size=10, bold=True, color=Colors.TEAL,
)
+ y += Inches(0.35)
# Downtime approach
if downtime:
- y += Inches(0.5)
+ if not tools:
+ y = max(y, Inches(1.3 + avail)) + Inches(0.15)
self._add_textbox(
slide, self.MARGIN, y,
Inches(12), Inches(0.35),
text=f"Downtime Approach: {downtime}",
- font_size=11, color=Colors.PRIMARY_TEXT,
+ font_size=10, color=Colors.PRIMARY_TEXT,
)
def add_operational_raci_slide(self, raci_items: list,
|