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/aZ2q*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<@TTUTC0&#O!^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:$7Q&#aiPQ-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-omq&#l'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,