diff --git a/advisor/plugins/aws-startup-advisor/fixtures/README.md b/advisor/plugins/aws-startup-advisor/fixtures/README.md index 244299ae..4728d1a0 100644 --- a/advisor/plugins/aws-startup-advisor/fixtures/README.md +++ b/advisor/plugins/aws-startup-advisor/fixtures/README.md @@ -21,7 +21,7 @@ python3 scripts/validate-migration-report.py \ --estimation-ai fixtures/estimation-ai-reference.json ``` -`estimation-*-reference.json` are trimmed snapshots aligned with the HTML fixture. Together they exercise security-baseline cross-checks, the security teaser, the verdict banner, and combined-TCO (`exec-tco`) requirements. +`estimation-*-reference.json` are trimmed snapshots aligned with the HTML fixture. Together they exercise security-baseline cross-checks, the security teaser, the verdict banner, and combined AWS monthly run-rate (`exec-tco`, legacy ID) requirements. ## Regression stub @@ -31,10 +31,10 @@ python3 scripts/validate-migration-report.py \ The fixture is also the worked example for the readability rules the validator now enforces (not just documents): -- **No numeric "Section N" headings.** Customer-facing `

`/`

` headings use plain titles (e.g. "Total Cost of Ownership", not "Section 1 — …"). The table of contents carries structure: executive sections in an ordered list, appendices in a separate lettered list to avoid double-numbering. +- **No numeric "Section N" headings.** Customer-facing `

`/`

` headings use plain titles (e.g. "Estimated AWS Monthly Run Rate", not "Section 1 — …"). The table of contents carries structure: executive sections in an ordered list, appendices in a separate lettered list to avoid double-numbering. - **No internal scoring trace.** Per-cluster mapping rationale lives in a collapsible `
` ("Why this mapping?") block, never a bare `Rubric:` line. - **Security teaser up top, full detail in the appendix.** `exec-security-teaser` carries a 2–3 line summary; the full control table and gap analysis are `appendix-security` / `appendix-security-gap`. -- **Consistent money formatting** (whole-dollar monthly figures; cents only where sub-dollar precision matters) and **expanded acronyms** (glossary in the assumptions section). +- **Consistent money formatting** (whole-dollar monthly figures; cents only where sub-dollar precision matters) and **expanded acronyms** (bordered two-column glossary table in the assumptions section). - **Accessible tables and diagram**: `` + `scope="col"` on tables; the ASCII architecture diagram is wrapped in a `
` with an `aria-label` text alternative and a `
`. - **Configuration provenance (`appendix-config`).** Four-column table: Question/assumption, Your choice, Source, Design consequence — populated from `preferences.json` `prompt` and `design_consequence` fields (see `references/shared/schema-preferences.md`). - **Ordered action lists.** `Key decisions ahead` and `Next steps` in `decision-summary` use `
    `, not `
      `. @@ -45,4 +45,4 @@ A few sections carry `appendix-` IDs but render in the executive flow by design ## What REPORT_OK means -`REPORT_OK | structure=complete` means required sections, TOC links, appendix depth, readability rules, and artifact-driven cost/TCO checks passed. It does **not** verify that every dollar figure in the HTML matches the JSON — verify numerics manually or in a future accuracy gate before executive sign-off. +`REPORT_OK | structure=complete` means required sections, TOC links, appendix depth, readability rules, and artifact-driven cloud-service run-rate checks passed. It does **not** verify that every dollar figure in the HTML matches the JSON — verify numerics manually or in a future accuracy gate before executive sign-off. diff --git a/advisor/plugins/aws-startup-advisor/fixtures/estimation-infra-reference.json b/advisor/plugins/aws-startup-advisor/fixtures/estimation-infra-reference.json index dfbbb421..e5dc0356 100644 --- a/advisor/plugins/aws-startup-advisor/fixtures/estimation-infra-reference.json +++ b/advisor/plugins/aws-startup-advisor/fixtures/estimation-infra-reference.json @@ -14,6 +14,16 @@ "free_controls": 0.5 } } + }, + "scenario_deltas": { + "premium": [ + "RDS Multi-AZ instead of single-AZ (+$21/mo) - removes the single-AZ assumption", + "Fargate provisioned headroom, 2x task count (+$12/mo)" + ], + "optimized": [ + "Drops NAT Gateway (-$18/mo) - tasks move to public subnets or VPC endpoints; security-posture change, not just savings", + "Assumes 1-year Compute Savings Plan (-$12/mo) - requires an upfront commitment decision" + ] } }, "current_costs": { @@ -24,19 +34,51 @@ "outcome": "conditional_go", "outcome_label": "Go, with conditions", "conditions": [ - "Confirm database availability — Multi-AZ was assumed, not confirmed (roughly 2x database cost factor)" + "Confirm database availability \u2014 Multi-AZ was assumed, not confirmed (roughly 2x database cost factor)" ], "decision_basis": { - "measured": ["GCP billing baseline ($165/mo infra)", "Cloud SQL disk size (10 GB)"], - "assumed": ["Database availability (Multi-AZ default)", "Cloud Run traffic (24/7 default)"], + "measured": [ + "GCP billing baseline ($165/mo infra)", + "Cloud SQL disk size (10 GB)" + ], + "assumed": [ + "Database availability (Multi-AZ default)", + "Cloud Run traffic (24/7 default)" + ], "unknown": [] }, "would_flip_if": [ "Single-AZ acceptable: AWS estimate drops further, strengthens go", "BigQuery must cut over in the same window: defer for specialist evidence" ], - "migrate_if": ["unified AWS operations"], - "stay_if": ["BigQuery must cut over in the same window as app infra and cannot use a phased analytics track"] + "migrate_if": [ + "unified AWS operations" + ], + "stay_if": [ + "BigQuery must cut over in the same window as app infra and cannot use a phased analytics track" + ], + "track_outcomes": [ + { + "track": "compute_database", + "outcome": "go", + "note": "Fargate + RDS; the phased backbone" + }, + { + "track": "ai_text", + "outcome": "go", + "note": "Nova Lite via provider adapter" + }, + { + "track": "ai_image", + "outcome": "conditional_go", + "note": "Keeps current provider via the adapter until the image-quality eval passes; does not gate other tracks" + }, + { + "track": "analytics", + "outcome": "defer_for_evidence", + "note": "BigQuery - specialist engagement, parallel track" + } + ] }, "cost_comparison": { "option_b_balanced": { diff --git a/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/.phase-status.json b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/.phase-status.json new file mode 100644 index 00000000..1fcfbc94 --- /dev/null +++ b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/.phase-status.json @@ -0,0 +1,15 @@ +{ + "migration_id": "0723-1900", + "last_updated": "2026-07-23T19:22:00Z", + "current_phase": "complete", + "run_mode": "decide", + "phases": { + "discover": "completed", + "clarify": "completed", + "design": "completed", + "estimate": "completed", + "workshop": "completed", + "generate": "pending", + "feedback": "completed" + } +} diff --git a/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/DECISION.md b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/DECISION.md new file mode 100644 index 00000000..ba87e495 --- /dev/null +++ b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/DECISION.md @@ -0,0 +1,37 @@ +# Migration Decision — GCP to AWS + +**Verdict: Go, with conditions** (phased migration · moderate complexity · medium confidence) + +Proceed with a phased migration on the Balanced cost scenario once the availability assumption is confirmed. + +## Costs (estimated monthly) + +| Tier | Est. Monthly AWS | +| -------------------------------------------- | ---------------- | +| Premium | $212/mo | +| **Balanced** (compare GCP to this row first) | **$155/mo** | +| Optimized | $118/mo | + +GCP baseline: estimated from resource configs (±20–30%, standing charges only). The stated $1K–$5K/mo spend band measures the whole bill and is not directly comparable. + +## Migrate if / Stay if + +- **Migrate if:** you want managed compute without Kubernetes operations; consolidating AI usage onto Bedrock matters this year. +- **Stay if:** your GCP committed-use discounts run through 2027. + +## Timeline + +Phased in dependency order if you execute — long pole: database migration, then cutover in a maintenance window (medium-complexity drivers; no week estimates — uncalibrated). + +## Top risks + +- Availability posture assumed single-AZ — confirm before cutover sizing. +- BigQuery deferred to specialist engagement; excluded from combined totals. + +## What this rests on + +Region, database size, and model detection extracted from Terraform and code; availability defaulted to single-AZ per dev-tier signals. Cached pricing dated 2026-07 (±5–10% infra). + +--- + +**Ready to execute?** Say "generate the Terraform and migration scripts" for the full execution pack. _Draft for review._ diff --git a/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/decision-report.html b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/decision-report.html new file mode 100644 index 00000000..cf0d944b --- /dev/null +++ b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/after-decide-complete/decision-report.html @@ -0,0 +1,153 @@ + + + + + +GCP to AWS Migration Assessment — Decision Report + + + +
      +
      +

      Migration Decision Summary ↑ contents

      +

      Go, with conditions

      +

      Execution shape: Phased migration · Complexity: Moderate

      +
      +
      Balanced estimateEst. $155/moPrimary AWS comparison scenario
      +
      Shape if you executePhasedLong pole: database migration (medium-complexity drivers)
      +
      Decision statusConditional goConfirm availability before cutover
      +
      +
      💡 AWS Activate Founders: up to $5,000 in credits — Apply for AWS Activate credits before incurring eligible charges.
      +

      Confidence: Medium — full basis in What This Assessment Rests On.

      +
      Recommendation: Proceed with a phased migration on the Balanced cost scenario once the availability assumption is confirmed.
      +

      Migrate if

      +
      • You want managed compute without Kubernetes operations
      • Consolidating AI usage onto Bedrock matters this year
      +

      Stay if

      +
      • Your GCP committed-use discounts run through 2027
      +
      + +
      +

      Current Stack ↑ contents

      + +
      Primary GCP services detected
      ServiceType
      Cloud Runcompute
      Cloud SQL (PostgreSQL 15)database
      Cloud Storagestorage
      +
      +
      +

      Cost Comparison ↑ contents

      + +
      Estimated monthly costs
      TierEst. Monthly AWS
      PremiumEst. $212/mo
      Balanced (compare GCP to this row first)Est. $155/mo
      OptimizedEst. $118/mo
      +

      ⚠ not comparable Your GCP baseline: estimated from resource configs (±20–30%, standing charges only). Your stated spend band ($1K–$5K/mo) measures your whole bill and is not directly comparable to the AWS figure above.

      +
      +
      +

      Timeline ↑ contents

      +

      Phased in dependency order if you execute — long pole: database migration, then cutover in a maintenance window (medium-complexity drivers; the plugin quotes no week estimates — uncalibrated).

      +
      +
      +

      Top Risks ↑ contents

      + + + + + +
      Highest-severity migration risks
      RiskImpactLikelihoodMitigation
      Availability posture assumed single-AZHighMediumConfirm the availability requirement before cutover sizing
      BigQuery excluded from combined totalsHighLowDeferred to specialist engagement; plan the analytics track in parallel
      +
      +
      +

      What This Assessment Rests On ↑ contents

      +

      Region, database size, and model detection extracted from your Terraform and code; availability defaulted to single-AZ per dev-tier signals. Cached pricing dated 2026-07 (±5–10% infra).

      +
      +
      +

      Ready to execute? ↑ contents

      +

      Say "generate the Terraform and migration scripts" and I'll produce the full execution pack (Terraform, migration scripts, rollback runbook, fill-in checklist) from this same analysis.

      +

      This decision report was generated without execution artifacts; the full migration report replaces it if you proceed.

      +
      +
      +
      Generated by GCP to AWS Migration Advisor — draft for review
      + + diff --git a/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/check_expected_decide.py b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/check_expected_decide.py new file mode 100644 index 00000000..85af14de --- /dev/null +++ b/advisor/plugins/aws-startup-advisor/fixtures/gcp-decision-gate/check_expected_decide.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""Assert a Decision-gate choice-A run landed in the decide-complete state. + +Locks PR #185's terminal semantics (current_phase complete + run_mode decide + +generate pending) and the decision-pack artifacts (decision-report.html passes +the validator in --mode decision; DECISION.md exists; no Generate artifacts). + +Usage: check_expected_decide.py +""" +from __future__ import annotations + +import json +import subprocess # nosec B404 — fixture asserter; runs only the committed validator via sys.executable +import sys +from pathlib import Path + +PLUGIN_ROOT = Path(__file__).resolve().parents[2] +VALIDATOR = PLUGIN_ROOT / "scripts" / "validate-migration-report.py" +FAILS: list[str] = [] + + +def check(cond: bool, msg: str) -> None: + if not cond: + FAILS.append(msg) + + +def main() -> int: + if len(sys.argv) != 2: + print(__doc__) + return 2 + run = Path(sys.argv[1]) + + # Terminal state: decide-complete, not failure, not in-flight. + ph_path = run / ".phase-status.json" + check(ph_path.exists(), "missing .phase-status.json") + if not ph_path.exists(): + print("FAIL") + [print(" -", f) for f in FAILS] + return 1 + ph = json.loads(ph_path.read_text()) + phases = ph.get("phases") or {} + check(ph.get("current_phase") == "complete", f"current_phase={ph.get('current_phase')}") + check(ph.get("run_mode") == "decide", f"run_mode={ph.get('run_mode')}") + check(phases.get("generate") == "pending", f"generate={phases.get('generate')}") + check(phases.get("estimate") == "completed", f"estimate={phases.get('estimate')}") + check(phases.get("workshop") == "completed", f"workshop={phases.get('workshop')}") + + # Decision pack exists; execution artifacts do not. + report = run / "decision-report.html" + check(report.exists(), "missing decision-report.html") + check((run / "DECISION.md").exists(), "missing DECISION.md") + check(not (run / "terraform").exists(), "terraform/ must not exist on a decide run") + check( + not any(run.glob("generation-*.json")), + "generation-*.json must not exist on a decide run", + ) + + # The decision report passes the validator in decision mode. + if report.exists(): + result = subprocess.run( # nosec B603 — list args, no shell, committed script path only + [sys.executable, str(VALIDATOR), str(report), "--mode", "decision", + "--no-require-toc"], + capture_output=True, + text=True, + ) + check( + result.returncode == 0, + f"decision-report.html fails --mode decision:\n{result.stdout}{result.stderr}", + ) + # Content locks (beyond structure): verdict typography (#173) and the + # not-comparable baseline rule (#175) must survive refactors, and the + # timeline must read as a band, not a committed schedule. + html = report.read_text() + check('class="verdict-headline"' in html, "missing verdict-headline element (#173 typography)") + check("not directly comparable" in html, "missing not-comparable baseline sentence (#175)") + check("if you execute" in html, 'timeline must be labeled "if you execute" (band, not schedule)') + + if FAILS: + print("FAIL") + [print(" -", f) for f in FAILS] + return 1 + print("PASS") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/advisor/plugins/aws-startup-advisor/fixtures/migration-report-reference.html b/advisor/plugins/aws-startup-advisor/fixtures/migration-report-reference.html index 6d3f1508..76428b9d 100644 --- a/advisor/plugins/aws-startup-advisor/fixtures/migration-report-reference.html +++ b/advisor/plugins/aws-startup-advisor/fixtures/migration-report-reference.html @@ -56,6 +56,7 @@ .toc li { margin: 0.2rem 0; font-size: 0.9rem; } .toc a { color: var(--blue); text-decoration: none; } .toc a:hover { text-decoration: underline; } + a:focus-visible, summary:focus-visible { outline: 3px solid #54aeff; outline-offset: 2px; border-radius: 2px; } .toc .toc-group { font-weight: 600; margin-top: 0.75rem; font-size: 0.85rem; color: #656d76; text-transform: uppercase; letter-spacing: 0.02em; } .tier-sub { font-size: 0.78rem; color: #656d76; font-style: italic; display: block; margin-top: 0.15rem; } .badge-conf { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.72rem; font-weight: 600; } @@ -69,11 +70,21 @@ details.why { margin: 0.4rem 0; font-size: 0.82rem; color: #656d76; } details.why summary { cursor: pointer; color: var(--blue); } details.why ul { margin: 0.4rem 0; padding-left: 1.1rem; } + .glossary-table th:first-child, .glossary-table td:first-child { width: 27%; background: #f6f8fa; } + .glossary-table td:first-child { color: var(--aws); font-weight: 600; } .cluster-block { border-left: 3px solid var(--blue); padding-left: 1rem; margin: 1rem 0; } footer { text-align: center; color: #656d76; font-size: 0.8rem; margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border); } + @media (max-width: 700px) { + .container { padding: 1rem 0.75rem 2rem; } + section { padding: 1rem; } + .metrics { grid-template-columns: 1fr; } + .toc ol, .toc ul.toc-appendix { columns: 1; } + table, .diagram { display: block; overflow-x: auto; } + } @media print { body { background: #fff; } - section { break-inside: avoid; } + .container { max-width: none; padding: 0; } + .metric, .callout, tr { break-inside: avoid; } .appendix-header { break-before: page; } } @@ -92,12 +103,12 @@

      GCP to AWS Migration Assessment

      Executive summary
      1. Migration Decision Summary
      2. -
      3. Total Cost of Ownership
      4. +
      5. Estimated AWS Monthly Run Rate
      6. Current Stack Overview
      7. Infrastructure Cost Comparison
      8. Architecture Overview
      9. Security Posture
      10. -
      11. Timeline & Effort
      12. +
      13. Migration Shape
      14. Top Risks
      15. What This Assessment Rests On
      16. Assumptions, Exclusions & Validation
      17. @@ -120,6 +131,7 @@

        GCP to AWS Migration Assessment

        Migration Decision Summary

        Go, with conditions

        Execution shape: Phased Migration · Complexity: Complex

        +

        By track: Compute + database: go. AI text: go. AI image: conditional — keeps the current provider via the adapter until the quality eval passes; does not gate the rest. Analytics: deferred — specialist track, parallel.

        • Condition: confirm database availability — Multi-AZ was assumed, not confirmed (roughly 2x database cost factor)
        • Condition: confirm CUD expiration date before committing a migration start date
        • @@ -127,9 +139,10 @@

          Migration Decision Summary

          Confidence: Medium — full basis in What This Assessment Rests On.

          - Recommendation: Migrate, phased over 10 weeks — estimated monthly savings of ~$503/month + Recommendation: Migrate, phased in dependency order — long pole is the database cutover, with a parallel AI track; estimated monthly savings of ~$503/month (-35%) combined infrastructure + AI, with BigQuery analytics deferred to a - specialist engagement. Eligible for up to $5K AWS Activate credits. + specialist engagement. Eligible for up to $5K + AWS Activate credits.
          @@ -139,19 +152,14 @@

          Migration Decision Summary

          Est. $112/mo AWS vs $165/mo GCP infra
          - Combined TCO (Balanced) + Combined AWS run rate (Balanced) -35% Est. $912/mo AWS vs $1,415/mo GCP (excl. BigQuery)
          - Timeline - 10 weeks - Infra phased + 3-week AI track (parallel) -
          -
          - Effort estimate - ~200 hrs - 120 infra + 80 AI engineering + Migration shape + Phased + Long pole: database cutover; AI track runs parallel
          AI projection @@ -161,12 +169,12 @@

          Migration Decision Summary

          Activate credits Up to $5K - Eligible Founders tier + Eligible Founders tier · Apply for credits
          - Specialist engagement required: BigQuery (sf_beach_analytics) has no automated AWS mapping from this plugin — engage your AWS account team or a data partner to evaluate the best analytics migration path. Analytics costs are excluded from TCO totals until that path is defined. This does not block phased migration of compute, database, storage, and AI; BigQuery planning runs in parallel on the timeline below. + Specialist engagement required: BigQuery (sf_beach_analytics) has no automated AWS mapping from this plugin — engage your AWS account team or a data partner to evaluate the best analytics migration path. Analytics costs are excluded from the estimated AWS monthly run rate until that path is defined. This does not block phased migration of compute, database, storage, and AI; BigQuery planning runs in parallel on the timeline below.
          @@ -176,7 +184,7 @@

          Migration Decision Summary

          Migrate if

          • You want unified AWS operations for containers, database, and Bedrock AI
          • -
          • Estimated combined TCO savings (~$503/month) and AWS Activate credits offset migration effort
          • +
          • Estimated combined cloud-service savings (~$503/month) and AWS Activate credits offset the cost of migrating
          • You can engage AWS account team for BigQuery analytics in parallel
          @@ -205,10 +213,10 @@

          Next steps

      - +
      -

      Total Cost of Ownership

      -

      Combined view of your infrastructure and AI estimated monthly costs. BigQuery excluded from both sides.

      +

      Estimated AWS Monthly Run Rate

      +

      Combined view of recurring infrastructure and AI cloud-service costs. BigQuery is excluded from both sides. This is not a total ownership-cost model: staffing, operations, support, and migration labor are not priced.

      @@ -296,6 +304,7 @@

      Infrastructure Cost Comparison

      + @@ -304,18 +313,21 @@

      Infrastructure Cost Comparison

      + + +
      Combined estimated monthly costs, GCP vs AWS (BigQuery excluded).
      Est. Monthly AWS vs GCP ($165/mo) Annual differencevs Balanced, for this stack
      $198 +20% (+$33) +$396RDS Multi-AZ instead of single-AZ (+$21/mo) — removes the single-AZ assumption; Fargate provisioned headroom (+$12/mo)
      Balanced
      Default scenario; Terraform implements this tier
      $112 -32% (-$53) -$636— anchor; matches generated Terraform
      Optimized
      Spot, reservations, storage trade-offs assumed
      $82 -50% (-$83) -$996Drops NAT Gateway (-$18/mo) — tasks move to public subnets or VPC endpoints, a posture change, not just savings; assumes a 1-year Compute Savings Plan (-$12/mo)
      @@ -388,34 +400,34 @@

      Security Posture

      -

      Timeline & Effort

      +

      Migration Shape

      -

      Infrastructure migration (10 weeks, phased)

      +

      Infrastructure migration (phased, in dependency order)

      - + - - - - - - + + + + + +
      Phased infrastructure migration schedule.
      PhaseWeeksActivities
      OrderPhaseActivities
      Setup1–2VPC, IAM, security baseline, CI/CD for Terraform, Bedrock model access
      PoC3–4Deploy networking + storage clusters; smoke test connectivity
      Infrastructure Deployment5–7RDS, Fargate, ALB; cross-cluster security groups
      Data Migration8Cloud SQL → RDS replication via Database Migration Service (DMS); GCS → S3 sync
      Cutover9Weekly maintenance window; DNS/traffic switch
      Validation10Cost validation; GCP teardown planning
      1SetupVPC, IAM, security baseline, CI/CD for Terraform, Bedrock model access
      2PoCDeploy networking + storage clusters; smoke test connectivity
      3Infrastructure DeploymentRDS, Fargate, ALB; cross-cluster security groups
      4Data MigrationCloud SQL → RDS replication via Database Migration Service (DMS); GCS → S3 sync
      5CutoverWeekly maintenance window; DNS/traffic switch
      6ValidationCost validation; GCP teardown planning
      -

      AI migration (3 weeks, parallel track)

      +

      AI migration (parallel track)

      - + - - - + + +
      Parallel AI migration schedule.
      PhaseWeekActivities
      OrderPhaseActivities
      Bedrock setup1Enable model access, IAM role, provider adapter with feature flag
      Staging validation2A/B comparison, prompt tuning, image quality review
      Production rollout310% → 50% → 100% traffic; monitor cost and latency
      1Bedrock setupEnable model access, IAM role, provider adapter with feature flag
      2Staging validationA/B comparison, prompt tuning, image quality review
      3Production rollout10% → 50% → 100% traffic; monitor cost and latency
      -

      Estimated effort: 120 hours (infra) + 80 hours (AI) = ~200 engineering hours

      +

      Duration drivers: database replication and cutover (binding), cross-cluster security groups, parallel AI evaluation per model. Stage order and go/no-go gates carry the schedule; the plugin quotes no week or hour estimates (uncalibrated).

      Cutover strategy: maintenance-window-weekly  |  Rollback: revert DNS to GCP Cloud Run; keep RDS read replica 48h

      @@ -473,9 +485,9 @@

      Pricing confidence

      Exclusions

        -
      • BigQuery analytics — AWS target path not yet selected by specialist; costs excluded from TCO totals (does not block other services)
      • +
      • BigQuery analytics — AWS target path not yet selected by specialist; costs excluded from the estimated AWS monthly run rate (does not block other services)
      • GCP egress during migration — billing export not available (billing_data_available: false)
      • -
      • Professional services / labor — engineering hours estimated but not priced
      • +
      • Professional services / labor — neither estimated nor priced (no calibrated effort data)
      • Dual-run period — temporary cost of running GCP + AWS concurrently not modeled
      • Dev-tier sizing — db.t4g.micro, single-AZ, on-demand Fargate unless upgraded
      @@ -487,11 +499,11 @@

      Terraform validation

      Glossary

      - - +
      Acronyms used in this report.
      + - + diff --git a/advisor/plugins/aws-startup-advisor/scripts/validate-migration-report.py b/advisor/plugins/aws-startup-advisor/scripts/validate-migration-report.py index 8abd75ed..9faf4d9d 100644 --- a/advisor/plugins/aws-startup-advisor/scripts/validate-migration-report.py +++ b/advisor/plugins/aws-startup-advisor/scripts/validate-migration-report.py @@ -41,6 +41,19 @@ "appendix-artifacts", ] +# Decision mode (decision-report.html rendered at the post-Estimate Decision +# gate, choice A): executive sections + CTA only — appendices are forbidden +# because no Generate artifacts exist yet. +DECISION_REQUIRED_SECTION_IDS = [ + "decision-summary", + "exec-assumptions", + "exec-services", + "exec-costs", + "exec-timeline", + "exec-risks", + "decision-cta", +] + OPTIONAL_SECTION_IDS = [ "exec-tco", "exec-architecture", @@ -63,11 +76,27 @@ # fixture" to "enforced gate", so a stray internal scoring trace or a patched # "Section 0/1b" heading fails the report instead of silently shipping. READABILITY_PATTERNS = [ + ( + r"(? "Why this mapping?" block', ), + ( + r"\d+\s*(?:–|-|to)\s*\d+\s*(?:engineering\s+|effort\s+)?hours\b", + "effort-hours range — the plugin has no calibrated effort data and " + "hour figures get pasted into budgets; communicate time as stage " + "sequence + duration drivers (migration-complexity.md § Provenance)", + ), + ( + r"(? dict[str, int]: return counts -def _validate_required_sections(html: str) -> list[str]: +def _validate_required_sections( + html: str, required_ids: list[str] | None = None +) -> list[str]: errors: list[str] = [] counts = _section_id_counts(html) - for section_id in REQUIRED_SECTION_IDS: + for section_id in required_ids if required_ids is not None else REQUIRED_SECTION_IDS: n = counts.get(section_id, 0) if n == 0: errors.append(f'missing required
      ') @@ -175,7 +268,9 @@ def _toc_hrefs(html: str) -> list[str]: return re.findall(r'href="#([^"]+)"', nav_match.group(1), re.IGNORECASE) -def _validate_toc(html: str) -> list[str]: +def _validate_toc(html: str, required_ids: list[str] | None = None) -> list[str]: + if required_ids is None: + required_ids = REQUIRED_SECTION_IDS errors: list[str] = [] hrefs = _toc_hrefs(html) if not hrefs: @@ -187,7 +282,7 @@ def _validate_toc(html: str) -> list[str]: errors.append(f'TOC broken link href="#{href}" — no matching
      ') # Every required section must be linked from the TOC. - for section_id in REQUIRED_SECTION_IDS: + for section_id in required_ids: if section_id in section_ids and section_id not in hrefs and hrefs: errors.append( f'TOC missing link to required section id="{section_id}" ' @@ -280,6 +375,23 @@ def _validate_readability(html: str) -> list[str]: return errors +def _validate_visual_contract(html: str) -> list[str]: + """Validate the shared report shell's minimum visual readability contract.""" + style_blocks = re.findall( + r"]*>(.*?)", + html, + re.DOTALL | re.IGNORECASE, + ) + if not style_blocks: + return ["visual readability: missing inline + + +
      +
      +

      Migration Decision Summary ↑ contents

      +

      Go, with conditions

      +

      Execution shape: Phased migration · Complexity: Moderate

      +
      +
      Balanced estimateEst. $155/moPrimary AWS comparison scenario
      +
      Shape if you executePhasedLong pole: database migration (medium-complexity drivers)
      +
      Decision statusConditional goConfirm availability before cutover
      +
      +
      💡 AWS Activate Founders: up to $5,000 in credits — Apply for AWS Activate credits before incurring eligible charges.
      +

      Confidence: Medium — full basis in What This Assessment Rests On.

      +
      Recommendation: Proceed with a phased migration on the Balanced cost scenario once the availability assumption is confirmed.
      +

      Migrate if

      +
      • You want managed compute without Kubernetes operations
      • Consolidating AI usage onto Bedrock matters this year
      +

      Stay if

      +
      • Your GCP committed-use discounts run through 2027
      +
      + +
      +

      Current Stack ↑ contents

      +
      Terms used in this report.
      TermMeaning
      TCOTotal Cost of Ownership
      Monthly run rateEstimated recurring cloud-service charges; excludes staffing, support, migration labor, and other ownership costs
      DMSAWS Database Migration Service
      OAICloudFront Origin Access Identity
      RTORecovery Time Objective
      +
      Primary GCP services detected
      ServiceType
      Cloud Runcompute
      Cloud SQL (PostgreSQL 15)database
      Cloud Storagestorage
      +
      +
      +

      Cost Comparison ↑ contents

      + +
      Estimated monthly costs
      TierEst. Monthly AWS
      PremiumEst. $212/mo
      Balanced (compare GCP to this row first)Est. $155/mo
      OptimizedEst. $118/mo
      +

      ⚠ not comparable Your GCP baseline: estimated from resource configs (±20–30%, standing charges only). Your stated spend band ($1K–$5K/mo) measures your whole bill and is not directly comparable to the AWS figure above.

      +
      +
      +

      Timeline ↑ contents

      +

      Phased in dependency order if you execute — long pole: database migration, then cutover in a maintenance window (medium-complexity drivers; the plugin quotes no week estimates — uncalibrated).

      +
      +
      +

      Top Risks ↑ contents

      + + + + + +
      Highest-severity migration risks
      RiskImpactLikelihoodMitigation
      Availability posture assumed single-AZHighMediumConfirm the availability requirement before cutover sizing
      BigQuery excluded from combined totalsHighLowDeferred to specialist engagement; plan the analytics track in parallel
      +
      +
      +

      What This Assessment Rests On ↑ contents

      +

      Region, database size, and model detection extracted from your Terraform and code; availability defaulted to single-AZ per dev-tier signals. Cached pricing dated 2026-07 (±5–10% infra).

      +
      +
      +

      Ready to execute? ↑ contents

      +

      Say "generate the Terraform and migration scripts" and I'll produce the full execution pack (Terraform, migration scripts, rollback runbook, fill-in checklist) from this same analysis.

      +

      This decision report was generated without execution artifacts; the full migration report replaces it if you proceed.

      +
      + +
      Generated by GCP to AWS Migration Advisor — draft for review
      + + diff --git a/migrate/plugins/migration-to-aws/fixtures/gcp-decision-gate/check_expected_decide.py b/migrate/plugins/migration-to-aws/fixtures/gcp-decision-gate/check_expected_decide.py new file mode 100644 index 00000000..85af14de --- /dev/null +++ b/migrate/plugins/migration-to-aws/fixtures/gcp-decision-gate/check_expected_decide.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""Assert a Decision-gate choice-A run landed in the decide-complete state. + +Locks PR #185's terminal semantics (current_phase complete + run_mode decide + +generate pending) and the decision-pack artifacts (decision-report.html passes +the validator in --mode decision; DECISION.md exists; no Generate artifacts). + +Usage: check_expected_decide.py +""" +from __future__ import annotations + +import json +import subprocess # nosec B404 — fixture asserter; runs only the committed validator via sys.executable +import sys +from pathlib import Path + +PLUGIN_ROOT = Path(__file__).resolve().parents[2] +VALIDATOR = PLUGIN_ROOT / "scripts" / "validate-migration-report.py" +FAILS: list[str] = [] + + +def check(cond: bool, msg: str) -> None: + if not cond: + FAILS.append(msg) + + +def main() -> int: + if len(sys.argv) != 2: + print(__doc__) + return 2 + run = Path(sys.argv[1]) + + # Terminal state: decide-complete, not failure, not in-flight. + ph_path = run / ".phase-status.json" + check(ph_path.exists(), "missing .phase-status.json") + if not ph_path.exists(): + print("FAIL") + [print(" -", f) for f in FAILS] + return 1 + ph = json.loads(ph_path.read_text()) + phases = ph.get("phases") or {} + check(ph.get("current_phase") == "complete", f"current_phase={ph.get('current_phase')}") + check(ph.get("run_mode") == "decide", f"run_mode={ph.get('run_mode')}") + check(phases.get("generate") == "pending", f"generate={phases.get('generate')}") + check(phases.get("estimate") == "completed", f"estimate={phases.get('estimate')}") + check(phases.get("workshop") == "completed", f"workshop={phases.get('workshop')}") + + # Decision pack exists; execution artifacts do not. + report = run / "decision-report.html" + check(report.exists(), "missing decision-report.html") + check((run / "DECISION.md").exists(), "missing DECISION.md") + check(not (run / "terraform").exists(), "terraform/ must not exist on a decide run") + check( + not any(run.glob("generation-*.json")), + "generation-*.json must not exist on a decide run", + ) + + # The decision report passes the validator in decision mode. + if report.exists(): + result = subprocess.run( # nosec B603 — list args, no shell, committed script path only + [sys.executable, str(VALIDATOR), str(report), "--mode", "decision", + "--no-require-toc"], + capture_output=True, + text=True, + ) + check( + result.returncode == 0, + f"decision-report.html fails --mode decision:\n{result.stdout}{result.stderr}", + ) + # Content locks (beyond structure): verdict typography (#173) and the + # not-comparable baseline rule (#175) must survive refactors, and the + # timeline must read as a band, not a committed schedule. + html = report.read_text() + check('class="verdict-headline"' in html, "missing verdict-headline element (#173 typography)") + check("not directly comparable" in html, "missing not-comparable baseline sentence (#175)") + check("if you execute" in html, 'timeline must be labeled "if you execute" (band, not schedule)') + + if FAILS: + print("FAIL") + [print(" -", f) for f in FAILS] + return 1 + print("PASS") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/migrate/plugins/migration-to-aws/fixtures/migration-report-reference.html b/migrate/plugins/migration-to-aws/fixtures/migration-report-reference.html index 6d3f1508..76428b9d 100644 --- a/migrate/plugins/migration-to-aws/fixtures/migration-report-reference.html +++ b/migrate/plugins/migration-to-aws/fixtures/migration-report-reference.html @@ -56,6 +56,7 @@ .toc li { margin: 0.2rem 0; font-size: 0.9rem; } .toc a { color: var(--blue); text-decoration: none; } .toc a:hover { text-decoration: underline; } + a:focus-visible, summary:focus-visible { outline: 3px solid #54aeff; outline-offset: 2px; border-radius: 2px; } .toc .toc-group { font-weight: 600; margin-top: 0.75rem; font-size: 0.85rem; color: #656d76; text-transform: uppercase; letter-spacing: 0.02em; } .tier-sub { font-size: 0.78rem; color: #656d76; font-style: italic; display: block; margin-top: 0.15rem; } .badge-conf { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.72rem; font-weight: 600; } @@ -69,11 +70,21 @@ details.why { margin: 0.4rem 0; font-size: 0.82rem; color: #656d76; } details.why summary { cursor: pointer; color: var(--blue); } details.why ul { margin: 0.4rem 0; padding-left: 1.1rem; } + .glossary-table th:first-child, .glossary-table td:first-child { width: 27%; background: #f6f8fa; } + .glossary-table td:first-child { color: var(--aws); font-weight: 600; } .cluster-block { border-left: 3px solid var(--blue); padding-left: 1rem; margin: 1rem 0; } footer { text-align: center; color: #656d76; font-size: 0.8rem; margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border); } + @media (max-width: 700px) { + .container { padding: 1rem 0.75rem 2rem; } + section { padding: 1rem; } + .metrics { grid-template-columns: 1fr; } + .toc ol, .toc ul.toc-appendix { columns: 1; } + table, .diagram { display: block; overflow-x: auto; } + } @media print { body { background: #fff; } - section { break-inside: avoid; } + .container { max-width: none; padding: 0; } + .metric, .callout, tr { break-inside: avoid; } .appendix-header { break-before: page; } } @@ -92,12 +103,12 @@

      GCP to AWS Migration Assessment

      Executive summary
      1. Migration Decision Summary
      2. -
      3. Total Cost of Ownership
      4. +
      5. Estimated AWS Monthly Run Rate
      6. Current Stack Overview
      7. Infrastructure Cost Comparison
      8. Architecture Overview
      9. Security Posture
      10. -
      11. Timeline & Effort
      12. +
      13. Migration Shape
      14. Top Risks
      15. What This Assessment Rests On
      16. Assumptions, Exclusions & Validation
      17. @@ -120,6 +131,7 @@

        GCP to AWS Migration Assessment

        Migration Decision Summary

        Go, with conditions

        Execution shape: Phased Migration · Complexity: Complex

        +

        By track: Compute + database: go. AI text: go. AI image: conditional — keeps the current provider via the adapter until the quality eval passes; does not gate the rest. Analytics: deferred — specialist track, parallel.

        • Condition: confirm database availability — Multi-AZ was assumed, not confirmed (roughly 2x database cost factor)
        • Condition: confirm CUD expiration date before committing a migration start date
        • @@ -127,9 +139,10 @@

          Migration Decision Summary

          Confidence: Medium — full basis in What This Assessment Rests On.

          - Recommendation: Migrate, phased over 10 weeks — estimated monthly savings of ~$503/month + Recommendation: Migrate, phased in dependency order — long pole is the database cutover, with a parallel AI track; estimated monthly savings of ~$503/month (-35%) combined infrastructure + AI, with BigQuery analytics deferred to a - specialist engagement. Eligible for up to $5K AWS Activate credits. + specialist engagement. Eligible for up to $5K + AWS Activate credits.
          @@ -139,19 +152,14 @@

          Migration Decision Summary

          Est. $112/mo AWS vs $165/mo GCP infra
          - Combined TCO (Balanced) + Combined AWS run rate (Balanced) -35% Est. $912/mo AWS vs $1,415/mo GCP (excl. BigQuery)
          - Timeline - 10 weeks - Infra phased + 3-week AI track (parallel) -
          -
          - Effort estimate - ~200 hrs - 120 infra + 80 AI engineering + Migration shape + Phased + Long pole: database cutover; AI track runs parallel
          AI projection @@ -161,12 +169,12 @@

          Migration Decision Summary

          Activate credits Up to $5K - Eligible Founders tier + Eligible Founders tier · Apply for credits
          - Specialist engagement required: BigQuery (sf_beach_analytics) has no automated AWS mapping from this plugin — engage your AWS account team or a data partner to evaluate the best analytics migration path. Analytics costs are excluded from TCO totals until that path is defined. This does not block phased migration of compute, database, storage, and AI; BigQuery planning runs in parallel on the timeline below. + Specialist engagement required: BigQuery (sf_beach_analytics) has no automated AWS mapping from this plugin — engage your AWS account team or a data partner to evaluate the best analytics migration path. Analytics costs are excluded from the estimated AWS monthly run rate until that path is defined. This does not block phased migration of compute, database, storage, and AI; BigQuery planning runs in parallel on the timeline below.
          @@ -176,7 +184,7 @@

          Migration Decision Summary

          Migrate if

          • You want unified AWS operations for containers, database, and Bedrock AI
          • -
          • Estimated combined TCO savings (~$503/month) and AWS Activate credits offset migration effort
          • +
          • Estimated combined cloud-service savings (~$503/month) and AWS Activate credits offset the cost of migrating
          • You can engage AWS account team for BigQuery analytics in parallel
          @@ -205,10 +213,10 @@

          Next steps

      - +
      -

      Total Cost of Ownership

      -

      Combined view of your infrastructure and AI estimated monthly costs. BigQuery excluded from both sides.

      +

      Estimated AWS Monthly Run Rate

      +

      Combined view of recurring infrastructure and AI cloud-service costs. BigQuery is excluded from both sides. This is not a total ownership-cost model: staffing, operations, support, and migration labor are not priced.

      @@ -296,6 +304,7 @@

      Infrastructure Cost Comparison

      + @@ -304,18 +313,21 @@

      Infrastructure Cost Comparison

      + + +
      Combined estimated monthly costs, GCP vs AWS (BigQuery excluded).
      Est. Monthly AWS vs GCP ($165/mo) Annual differencevs Balanced, for this stack
      $198 +20% (+$33) +$396RDS Multi-AZ instead of single-AZ (+$21/mo) — removes the single-AZ assumption; Fargate provisioned headroom (+$12/mo)
      Balanced
      Default scenario; Terraform implements this tier
      $112 -32% (-$53) -$636— anchor; matches generated Terraform
      Optimized
      Spot, reservations, storage trade-offs assumed
      $82 -50% (-$83) -$996Drops NAT Gateway (-$18/mo) — tasks move to public subnets or VPC endpoints, a posture change, not just savings; assumes a 1-year Compute Savings Plan (-$12/mo)
      @@ -388,34 +400,34 @@

      Security Posture

      -

      Timeline & Effort

      +

      Migration Shape

      -

      Infrastructure migration (10 weeks, phased)

      +

      Infrastructure migration (phased, in dependency order)

      - + - - - - - - + + + + + +
      Phased infrastructure migration schedule.
      PhaseWeeksActivities
      OrderPhaseActivities
      Setup1–2VPC, IAM, security baseline, CI/CD for Terraform, Bedrock model access
      PoC3–4Deploy networking + storage clusters; smoke test connectivity
      Infrastructure Deployment5–7RDS, Fargate, ALB; cross-cluster security groups
      Data Migration8Cloud SQL → RDS replication via Database Migration Service (DMS); GCS → S3 sync
      Cutover9Weekly maintenance window; DNS/traffic switch
      Validation10Cost validation; GCP teardown planning
      1SetupVPC, IAM, security baseline, CI/CD for Terraform, Bedrock model access
      2PoCDeploy networking + storage clusters; smoke test connectivity
      3Infrastructure DeploymentRDS, Fargate, ALB; cross-cluster security groups
      4Data MigrationCloud SQL → RDS replication via Database Migration Service (DMS); GCS → S3 sync
      5CutoverWeekly maintenance window; DNS/traffic switch
      6ValidationCost validation; GCP teardown planning
      -

      AI migration (3 weeks, parallel track)

      +

      AI migration (parallel track)

      - + - - - + + +
      Parallel AI migration schedule.
      PhaseWeekActivities
      OrderPhaseActivities
      Bedrock setup1Enable model access, IAM role, provider adapter with feature flag
      Staging validation2A/B comparison, prompt tuning, image quality review
      Production rollout310% → 50% → 100% traffic; monitor cost and latency
      1Bedrock setupEnable model access, IAM role, provider adapter with feature flag
      2Staging validationA/B comparison, prompt tuning, image quality review
      3Production rollout10% → 50% → 100% traffic; monitor cost and latency
      -

      Estimated effort: 120 hours (infra) + 80 hours (AI) = ~200 engineering hours

      +

      Duration drivers: database replication and cutover (binding), cross-cluster security groups, parallel AI evaluation per model. Stage order and go/no-go gates carry the schedule; the plugin quotes no week or hour estimates (uncalibrated).

      Cutover strategy: maintenance-window-weekly  |  Rollback: revert DNS to GCP Cloud Run; keep RDS read replica 48h

      @@ -473,9 +485,9 @@

      Pricing confidence

      Exclusions

        -
      • BigQuery analytics — AWS target path not yet selected by specialist; costs excluded from TCO totals (does not block other services)
      • +
      • BigQuery analytics — AWS target path not yet selected by specialist; costs excluded from the estimated AWS monthly run rate (does not block other services)
      • GCP egress during migration — billing export not available (billing_data_available: false)
      • -
      • Professional services / labor — engineering hours estimated but not priced
      • +
      • Professional services / labor — neither estimated nor priced (no calibrated effort data)
      • Dual-run period — temporary cost of running GCP + AWS concurrently not modeled
      • Dev-tier sizing — db.t4g.micro, single-AZ, on-demand Fargate unless upgraded
      @@ -487,11 +499,11 @@

      Terraform validation

      Glossary

      - - +
      Acronyms used in this report.
      + - + diff --git a/migrate/plugins/migration-to-aws/scripts/validate-migration-report.py b/migrate/plugins/migration-to-aws/scripts/validate-migration-report.py index fee13278..00aba691 100644 --- a/migrate/plugins/migration-to-aws/scripts/validate-migration-report.py +++ b/migrate/plugins/migration-to-aws/scripts/validate-migration-report.py @@ -41,6 +41,19 @@ "appendix-artifacts", ] +# Decision mode (decision-report.html rendered at the post-Estimate Decision +# gate, choice A): executive sections + CTA only — appendices are forbidden +# because no Generate artifacts exist yet. +DECISION_REQUIRED_SECTION_IDS = [ + "decision-summary", + "exec-assumptions", + "exec-services", + "exec-costs", + "exec-timeline", + "exec-risks", + "decision-cta", +] + OPTIONAL_SECTION_IDS = [ "exec-tco", "exec-architecture", @@ -63,11 +76,27 @@ # fixture" to "enforced gate", so a stray internal scoring trace or a patched # "Section 0/1b" heading fails the report instead of silently shipping. READABILITY_PATTERNS = [ + ( + r"(? "Why this mapping?" block', ), + ( + r"\d+\s*(?:–|-|to)\s*\d+\s*(?:engineering\s+|effort\s+)?hours\b", + "effort-hours range — the plugin has no calibrated effort data and " + "hour figures get pasted into budgets; communicate time as stage " + "sequence + duration drivers (migration-complexity.md § Provenance)", + ), + ( + r"(? dict[str, int]: return counts -def _validate_required_sections(html: str) -> list[str]: +def _validate_required_sections( + html: str, required_ids: list[str] | None = None +) -> list[str]: errors: list[str] = [] counts = _section_id_counts(html) - for section_id in REQUIRED_SECTION_IDS: + for section_id in required_ids if required_ids is not None else REQUIRED_SECTION_IDS: n = counts.get(section_id, 0) if n == 0: errors.append(f'missing required
      ') @@ -175,7 +268,9 @@ def _toc_hrefs(html: str) -> list[str]: return re.findall(r'href="#([^"]+)"', nav_match.group(1), re.IGNORECASE) -def _validate_toc(html: str) -> list[str]: +def _validate_toc(html: str, required_ids: list[str] | None = None) -> list[str]: + if required_ids is None: + required_ids = REQUIRED_SECTION_IDS errors: list[str] = [] hrefs = _toc_hrefs(html) if not hrefs: @@ -187,7 +282,7 @@ def _validate_toc(html: str) -> list[str]: errors.append(f'TOC broken link href="#{href}" — no matching
      ') # Every required section must be linked from the TOC. - for section_id in REQUIRED_SECTION_IDS: + for section_id in required_ids: if section_id in section_ids and section_id not in hrefs and hrefs: errors.append( f'TOC missing link to required section id="{section_id}" ' @@ -280,6 +375,23 @@ def _validate_readability(html: str) -> list[str]: return errors +def _validate_visual_contract(html: str) -> list[str]: + """Validate the shared report shell's minimum visual readability contract.""" + style_blocks = re.findall( + r"]*>(.*?)", + html, + re.DOTALL | re.IGNORECASE, + ) + if not style_blocks: + return ["visual readability: missing inline
      Terms used in this report.
      TermMeaning
      TCOTotal Cost of Ownership
      Monthly run rateEstimated recurring cloud-service charges; excludes staffing, support, migration labor, and other ownership costs
      DMSAWS Database Migration Service
      OAICloudFront Origin Access Identity
      RTORecovery Time Objective