Skip to content

feat(gcp-to-aws): add Elastic Beanstalk as migration target for App E… - #101

Open
azpaulp wants to merge 16 commits into
awslabs:mainfrom
azpaulp:feat/elastic-beanstalk-gcp
Open

feat(gcp-to-aws): add Elastic Beanstalk as migration target for App E…#101
azpaulp wants to merge 16 commits into
awslabs:mainfrom
azpaulp:feat/elastic-beanstalk-gcp

Conversation

@azpaulp

@azpaulp azpaulp commented Jul 1, 2026

Copy link
Copy Markdown

…ngine workloads

Problem

Add AWS Elastic Beanstalk as a PaaS-to-PaaS mapping for Google App Engine resources. App Engine Standard/Flexible maps to EB by default, with user override to Fargate or Lambda via a new Clarify question (Q7b). Because EB is a first-class compute target, it is wired through every phase the other compute targets use — discover → clarify → design → estimate → generate — so a migration produces a complete design, cost estimate, and Terraform, not just a mapping.

Solution

Routing (Clarify + Design)

  • App Engine → EB is a conditional fast-path (deterministic when compute_model is absent or "managed_platform"); Q7b = container_orchestration → Fargate, serverless → Lambda. Q7b fires only when App Engine is in inventory.
  • The preferred-target enforcement is gated on compute_model, so an explicit Q7b = B/C choice is never silently reversed back to EB.
  • Cloud Run remains unconditionally mapped to Fargate (unaffected); compute_model does not touch it.
  • Multi-cloud portability (Q5) overrides App Engine → EB. When Q5 = multi-cloud, compute: "eks" is recorded, Q7b does not fire, and App Engine routes to EKS — overriding both the EB default and the container-path Fargate. This mirrors the existing Q8/GKE multi-cloud early-exit, so the two portability signals can no longer disagree. Applies on both the IaC and billing design paths.

Per-service fan-out (Design)

  • App Engine workload config (runtime, instance_class, scaling, env_variables) lives on the google_app_engine_standard/flexible_app_version resources, not the parent. The design fan-out emits one EB environment per App Engine service (grouping a service's versions), sized from that service's own scaling block + instance_class (the repo's "size from the source resource" pattern — Q6 availability governs databases only).
  • The *_app_version resources are config sources (Skip Mappings — read for config, not emitted as standalone AWS resources).

Estimate

  • EB cost row (EC2 × min_instances × 730 + ALB when LoadBalanced; $0 service fee), EB log-volume heuristic, EB EC2 in the Graviton rollup, and an ALB double-count carve-out (EB manages its own LB).

Generate

  • EB routes to compute.tf; emits aws_elastic_beanstalk_application + aws_elastic_beanstalk_environment (instance profile, LoadBalancerType=application, data-source solution stack, VPC placement — private+ALB for LoadBalanced, public subnet for SingleInstance).
  • Migration script deploys the app source (EB builds from the Dockerfile at deploy time — no ECR push).

Discover

  • Captures App Engine version fields (service, runtime, instance_class, scaling, serving_status, project, env_variables) into the inventory.

Scope note: TF-less App Engine (gcloud/app-code-only discovery) is a #149 follow-up; the "Discovery Inputs" note in elastic-beanstalk.md documents which discovery paths produce a full vs. coarse vs. no EB mapping today. This branch is merged up to current origin/main (including the #170/#171 Clarify interaction-contract rewrite).

Also fixes pre-existing inaccuracies in compute.md examples where Cloud Run and Cloud Functions were incorrectly shown as rubric-evaluated (they're deterministic fast-path mappings).

Type of Change

  • New plugin/power/tool
  • Bug fix

Team Folder

  • advisor/
  • migrate/
  • Other: ___

Checklist

  • I have read the CONTRIBUTING.md guidelines
  • My changes do not include hardcoded secrets, credentials, or internal-only content
  • I have run mise run build locally and it passes
  • I have updated documentation if needed
  • My changes are scoped to my team's folder only

azpaulp added 2 commits July 1, 2026 13:29
…ngine workloads

Add AWS Elastic Beanstalk as a PaaS-to-PaaS mapping for Google App Engine
resources. App Engine Standard/Flexible now maps to EB by default, with
user override to Fargate or Lambda via a new Clarify question (Q7b).

Key decisions:
- App Engine → EB is a conditional fast-path (deterministic when
  compute_model is absent or "managed_platform")
- Cloud Run remains unconditionally mapped to Fargate (unaffected)
- Q7b only fires when App Engine is in inventory
- EB design reference provides platform detection, configuration,
  deployment policies, and App Engine feature mapping
- google_app_engine_standard/flexible_app_version added to Skip Mappings

Also fixes pre-existing inaccuracies in compute.md examples where Cloud
Run and Cloud Functions were incorrectly shown as rubric-evaluated
(they're deterministic fast-path mappings).
Fix pre-existing inaccuracies where Cloud Run and Cloud Functions
examples showed rubric-based evaluation (inferred) when they actually
use deterministic fast-path mappings. Replace the single Output Schema
with two examples showing both deterministic and inferred patterns.

Also add compute_model to the User Preference criterion description
in design-infra.md.
@azpaulp
azpaulp requested a review from a team as a code owner July 1, 2026 18:23
azpaulp added 2 commits July 13, 2026 15:31
- elastic-beanstalk.md: drop stale version matrix, keep platform names +
  link to EB supported-platforms docs (versions rot quickly)
- reconcile Tomcat as a separate platform from Java SE (Corretto)
- correct port guidance: AL2023 proxies to 5000/PORT by default; app must
  bind the advertised port or first-deploy health checks fail
- verify secrets namespace (aws:elasticbeanstalk:application:environmentsecrets)
  and pin platform cutoff to 2025-03-26
- VPC: ALB in public subnets, instances in private subnets
- make sizing consistent (LoadBalanced -> t3.medium+) in EB output schema
  and compute.md Example 4a
- treat google_app_engine_*_app_version as config sources (runtime/
  instance_class/scaling), not skips; one EB environment per App Engine
  service so multi-service apps don't collapse
- pricing-cache.md: label EB estimate rows as RDS db.t4g.* (not Aurora)
awslabs#101)

Addresses remaining EB review feedback from PR awslabs#101, focused on the
App Engine → Elastic Beanstalk mapping (reviewer's main concern).

App Engine workload config (runtime, instance_class, scaling, env) lives
on the google_app_engine_{standard,flexible}_app_version resources, not
the parent google_app_engine_application. The prior pass documented this
but the design traversal didn't act on it, so a multi-service app still
collapsed into one mapping.

- classification-rules.md: reclassify app_version resources from Skip to
  SECONDARY config sources for the parent's EB mapping
- design-infra.md: add an App Engine fan-out step in Pass 1 that scans the
  full inventory for app_version resources (they link to the parent only
  by shared project, not by ID, so serves[]/clustering is unreliable),
  groups by service, and emits one EB environment per service (picking the
  serving/most-recent version per service)
- elastic-beanstalk.md: rework 'config comes from Terraform' section and
  output schema to a per-service example; drive instance_type from
  environment type (instance_class / Flexible resources block as a
  right-sizing nudge only), listing all three scaling blocks
- fast-path.md / compute.md: reconcile the notes and examples with the
  fan-out mechanism; stop showing runtime on the parent resource

Verified field names and the parent-linkage behavior against the
Terraform Google provider docs. Docs/reference only; no runtime code.

mise build tasks pass locally (lint:md, lint:types, lint:frontmatter,
test 45/45, fmt:check, bandit, gitleaks); semgrep not installed locally,
runs in CI.
@azpaulp

azpaulp commented Jul 14, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough review, @shinmc — all eight comments are addressed. Summary:

EB reference accuracy (verified against the current EB docs, not approximated):

  • Port — clarified that AL2023 forwards to 5000/$PORT by default and that the app must bind the advertised port or fail first-deploy health checks; recommend reading $PORT (as App Engine apps already do) or setting the PORT property.
  • Platform versions — dropped the stale version matrix; kept platform names and linked to the EB supported-platforms docs.
  • Tomcat — now a separate platform from Java SE (Corretto) in both the platform and detection tables.
  • Secrets — corrected to aws:elasticbeanstalk:application:environmentsecrets, supported on platform versions on/after 2025-03-26 (plus the JSON-key-extraction caveat).
  • VPC — ALB in public subnets, instances in private subnets.
  • Sizing — LoadBalanced→t3.medium+ / SingleInstance→t3.small is now consistent across the output schema, compute.md Example 4a, and the Sizing Defaults table.
  • Pricing cache — EB estimate rows relabeled as RDS db.t4g.* (were mislabeled Aurora).

App Engine → EB mapping (comment on fast-path.md) — fixed at the mechanism level, not just the prose:

  • Reclassified google_app_engine_{standard,flexible}_app_version from Skip Mappings to config sources, and added an explicit App Engine fan-out step to the design traversal so the mapping emits one EB environment per service.
  • Verified against the Terraform provider docs that app_version resources link to the parent only via shared project (no ID reference) — so the fan-out locates them by scanning the full inventory rather than relying on serves[]/clustering, which would have left the multi-service collapse unfixed.
  • Grouped by service and pick the serving/most-recent version, so distinct services don't collapse and multiple versions of one service don't over-produce environments.
  • Fixed the examples/output schema that incorrectly showed runtime on the parent.

This touched two files beyond the commented lines — phases/design/design-infra.md (the fan-out step) and clustering/terraform/classification-rules.md (reclassifying the app_version types) — which is what makes the fix actually run rather than just read correctly.

Testing: ran the mise build tasks locally — lint:md (0 errors), lint:types, lint:frontmatter, test (45/45), fmt:check, and the bandit/gitleaks scans all pass. Changes are documentation/reference only (no runtime code). AWS specifics were confirmed against the live EB docs and the Terraform Google provider docs rather than approximated.

Resolves conflicts in clarify.md from PR awslabs#127 (assumption-sheet wizard),
which restructured the clarify phase. Re-applied the Q7b (App Engine
compute model) additions onto main's new structure:
- Category C firing rule + reference-file range (Q7b–Q11)
- Q7b row in the Step 3 Disposition Catalog (PROPOSED/N-A)
- Q7b early-exit rule (N/A when no App Engine)
- compute_model constraint in the example preferences.json
- Q7b rows in the Answer Combination Triggers and Defaults tables

SKILL.md auto-merged (EB/App Engine additions preserved). All other
App Engine → EB work from this branch is unaffected by the merge.

Verified: fmt:check, lint:md, lint:types, test (57/57),
lint:frontmatter, gitleaks all pass.

@shinmc shinmc left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

herosjourney

This comment was marked as duplicate.

@herosjourney

Copy link
Copy Markdown
Contributor

Review: feat(gcp-to-aws): add Elastic Beanstalk as migration target for App Engine

(Please disregard the earlier bold-rendered review — formatting issue)

Overall this is a well-scoped, logically coherent PR. The routing logic is clean, the bug fixes to compute.md examples are correct, and there are no contradictions with the existing heroku-to-aws skill on main (both align on EB as the default PaaS-to-PaaS target). A few observations below.


1. elastic-beanstalk.md load condition is slightly broader than Q7b fire condition

File: SKILL.md (conditional reference table)

The load condition for elastic-beanstalk.md is:

google_app_engine_application in inventory OR compute_model == "managed_platform" in preferences

But Q7b only fires when App Engine is in the inventory. This means if preferences.json carries over from a previous run (or is manually created) with compute_model: "managed_platform" but no App Engine in the current inventory, the EB reference still loads unnecessarily. Low risk, but consider tightening the condition to require App Engine in inventory (the AND case) rather than the OR.


2. Eliminators table: naming asymmetry

File: references/design-refs/compute.md — Eliminators table

The new row reads:

| App Engine | Elastic Beanstalk | compute_model: "container_orchestration" or "serverless" → do not use EB |

Every other row in this table uses the GCP service as the source of a failed attempt at the AWS target (e.g., "Cloud Run → Lambda" means "Cloud Run tried to map to Lambda but hit a blocker"). This row is slightly different — the trigger is a user preference rather than a technical constraint. Consider a brief inline note like "(preference override, not technical blocker)" to help readers scan the table consistently.


3. No EB Worker Tier guidance

File: references/design-refs/elastic-beanstalk.md

The heroku-to-aws skill on main explicitly states: "EB Worker tier (SQS-based polling) is NOT used." This PR's elastic-beanstalk.md doesn't address the Worker Tier at all. For App Engine this is fine today (all App Engine services are web-facing), but if the skill later maps background task queues or Cloud Tasks to EB, the omission could lead to the Worker Tier being selected. Consider adding a brief note:

EB Worker Tier is not used by this skill. All App Engine services map to WebServer tier environments (SingleInstance or LoadBalanced).


4. Fan-out logic complexity in design-infra.md

File: references/phases/design/design-infra.md

The App Engine fan-out step (item 4 in Pass 1) is the densest paragraph in the file — scanning the entire inventory, matching by shared project, grouping by service, picking serving version, handling missing app_version resources, etc. This is thorough but may be difficult for an LLM to execute reliably in a single pass. Consider:

  • Splitting it into a dedicated sub-file (e.g., design-appengine-fanout.md) loaded conditionally when App Engine is present, similar to how design-eks.md handles the EKS branch.
  • Or at minimum, adding a numbered step list instead of the single-paragraph wall of text.

5. Output Schema section: minor clarity gap

File: references/design-refs/compute.md — Output Schema

The deterministic example correctly omits rubric_applied, while the inferred example includes it. This is consistent but implicit. A one-liner above the examples would help:

Deterministic mappings omit rubric_applied; inferred mappings include it.


6. Correctness of existing example fixes (positive note)

The corrections to Examples 1 and 2a (Cloud Run and Cloud Functions now shown as deterministic via fast-path rather than inferred via rubric) are accurate and resolve a pre-existing inconsistency with the Direct Mappings table. Good catch.


Summary

No blocking issues. The PR is internally consistent, does not conflict with heroku-to-aws, and the routing logic (Q7b → compute_model → fast-path condition → EB or rubric fallback) is sound. The notes above are improvements for robustness and readability.

- **Kubernetes orchestration explicitly required** (`kubernetes = "eks-managed"` or `"eks-or-ecs"` in `preferences.json`) → EKS
- **Default / no explicit K8s preference** (`kubernetes = "ecs-fargate"` or absent):
- → **Fargate** (absent kubernetes preference resolves to Fargate, not EKS — teams that want EKS answer A or B in Clarify)
- If `gcp-resource-inventory.json` contains `google_container_cluster` → EKS (IaC signal shows K8s workload)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new branch defaults GKE to EKS when a cluster is present, but it contradicts with our Clarify phase Q8, which seeks to steer the recommendation to ECS, when the user answers "frustrated with K8s" to that question. In other words we seem to be sending conflicting signals to LLM with this recommendation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with this. The new GKE guidance fights what Clarify Q8 is trying to do.

On main, if the user picks Fargate (or doesn’t say they want Kubernetes), we map GKE to Fargate. This PR changes that so “there’s a GKE cluster in the inventory” alone pushes the mapping to EKS — even when the user just said they’re frustrated with K8s and want Fargate.

There’s also a contradiction in the same file: the rubric still says “don’t default to EKS,” but the new Signals bullet does exactly that when a cluster is present.

I’d revert that bullet back to main’s Fargate default, and only recommend EKS when the user explicitly asks for it in Q8 (or when multi-cloud already forces EKS).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in the latest push. Reverted the google_container_cluster → EKS bullet back to the Fargate default; an absent/ecs-fargate Kubernetes preference now resolves to Fargate (EKS only when the user explicitly asks via Q8 A/B), so it no longer contradicts Q8's "frustrated with K8s → Fargate" steer. The file's 6-criteria rubric ("absent → Fargate, do not default to EKS") and the GKE Signals bullet now agree. Thanks for catching this. It was an out-of-scope regression in the EB branch.

@herosjourney

Copy link
Copy Markdown
Contributor

Question on intended audience, from the author of #149 (gcloud live discovery): are TF-less App Engine shops in scope for this mapping?

As written, #101 is IaC-path work — the App Engine → EB fast-path triggers off resources classified from Terraform (google_app_engine_standard/flexible_app_version etc.). That's correct and self-contained. But App Engine's demographic skews heavily toward teams that deploy with gcloud app deploy and have never written Terraform — arguably more so than any other GCP compute service. For those users, once #149 lands, discovery will come from the live gcloud path, and that path currently has no App Engine capture:

  • Fast path (Cloud Asset Inventory): appengine.googleapis.com/* assets are counted in live_metadata.unmapped_asset_types — so the user is told App Engine exists in the project, but no inventory resource is produced, and Q7b/this mapping never fire.
  • Per-service fallback mode (CAI disabled): there is no gcloud app row in the capture whitelist, so App Engine is fully invisible.

So the question decides the follow-up:

  1. If TF-less users are in scope (I'd argue they're most of the audience for this feature): the live capture set needs gcloud app services list + gcloud app versions list --format=json(...) rows and a parse mapping into the inventory's compute category, so App Engine resources exist for Q7b and the EB fast-path to act on. I'm happy to carry that as a follow-up to feat(gcp-to-aws): live discovery via the authenticated gcloud CLI (stacked on #148) #149 once both PRs land — flagging it here so the two don't ship assuming the other covers it.
  2. If this is deliberately IaC-only for v1: worth one line in the design ref or SKILL.md saying so, plus the live path keeping App Engine in unmapped_asset_types with its existing disclosure — then nothing else is needed.

Either answer is fine — mainly making sure the gap between the two PRs is a decision rather than an accident.

azpaulp added 2 commits July 21, 2026 09:56
Revert an out-of-scope GKE routing regression and apply reviewer polish
(herosjourney, icarthick).

- compute.md: revert the GKE Signals bullet to main's Fargate default — a
  google_container_cluster in inventory no longer forces EKS over the user's
  Fargate/Q8 preference (flagged by two reviewers; also contradicted the
  file's own 6-criteria rubric). Add 'preference override, not a technical
  blocker' note to the App Engine Eliminators row; add a one-line note that
  deterministic mappings omit rubric_applied.
- elastic-beanstalk.md: add 'Discovery Inputs' section clarifying Terraform
  (full per-service EB) vs billing (coarse) vs app-code/gcloud-live (not yet
  wired — awslabs#149 follow-up); add Worker-tier note (skill uses WebServer tier
  only, aligning with heroku-to-aws).
- design-billing.md: App Engine billing row Fargate -> Elastic Beanstalk so
  the billing path matches the new default.
- design-infra.md: convert the App Engine fan-out step from a dense paragraph
  to an ordered sub-step list for reliable single-pass execution.
- SKILL.md: tighten elastic-beanstalk.md load condition — require App Engine
  in inventory; skip even if compute_model is set.

Verified: fmt:check, lint:md, lint:types, test 57/57, lint:frontmatter,
gitleaks all pass.
…k-gcp

# Conflicts:
#	migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md
#	migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md
#	migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md
@azpaulp

azpaulp commented Jul 21, 2026

Copy link
Copy Markdown
Author

Thanks for the excellent feedback! I am preparing an update to this PR to address it.

…e + sizing)

Third-round PR awslabs#101 review response. Makes the App Engine → Elastic Beanstalk
feature complete across all phases and resolves the reviewer blockers.

Design/routing:
- Replace the Q6-availability EB sizing rule with source-config sizing (the
  repo's established pattern): environment type + instance size derive
  per-service from the app_version's own scaling block and instance_class
  (Standard) / resources block (Flexible). Q6 governs databases only.
- Fix the preferred-target reversal: the PaaS/managed-platform row is gated on
  compute_model (absent/managed_platform); Q7b=B/C keeps its rubric-selected
  Fargate/Lambda target (explicit enforcement exemption).
- Per-service unique gcp_address (parent TF address # service); carry
  min_instances + max_instances through design→generate.

Estimate:
- Add an Elastic Beanstalk cost row (EC2 × min_instances × 730 + ALB when
  LoadBalanced; /bin/zsh service fee), EB log-volume heuristic, and EB EC2 in the
  Graviton rollup. ALB row carves out LoadBalanced EB envs to avoid
  double-counting.

Generate:
- Route EB to compute.tf; emit aws_elastic_beanstalk_application +
  _environment with setting blocks (InstanceType, EnvironmentType,
  LoadBalancerType=application for ALB, ASG Min/MaxSize, IAM instance profile,
  VPC — public subnet for SingleInstance, private+ALB for LoadBalanced, env
  vars). Resolve solution_stack_name at generate time (do not paste the label).
- Migration script: shared always-emitted preamble + independent ECR and EB
  source-deploy sections (EB builds from Dockerfile at deploy time — no ECR
  push); reconcile the IaC model (Terraform provisions, CLI deploys).

Discovery: capture App Engine version fields (service, runtime, instance_class,
scaling, serving_status, project, env_variables) into the inventory.

Verified: fmt:check, lint:md (0), lint:types, test 57/57, lint:frontmatter,
gitleaks all pass. Reviewed across two adversarial passes; both BLOCKERs and
six follow-on findings resolved.
@azpaulp

azpaulp commented Jul 21, 2026

Copy link
Copy Markdown
Author

Pushed an update that takes App Engine → Elastic Beanstalk from a design-phase mapping to a complete end-to-end feature, and addresses the follow-on review feedback.

What prompted the expansion: review surfaced that EB was only wired into Design — an App Engine → EB migration would have produced no cost line and no Terraform. Rather than ship a design-only feature, this wires EB through the same phases every other compute target uses.

Design / routing

  • Preferred-target enforcement is now gated on compute_model, so an explicit Q7b = container_orchestration/serverless choice routes to Fargate/Lambda and is not silently reversed back to EB (previously the "App Engine source" trigger could).
  • EB sizing now derives per service from the app_version's own scaling block + instance_class (the repo's "size from the source resource" pattern, same as Cloud Run → Fargate). Dropped the earlier Q6-availability approach — Q6 governs databases only.
  • One EB environment per App Engine service (a service's versions collapse to one); *_app_version resources are config sources, not standalone targets.

Estimate — added an EB cost row (EC2 × min_instances × 730 + ALB when LoadBalanced; $0 service fee), EB log-volume, EB in the Graviton rollup, and an ALB double-count carve-out (EB manages its own LB).

Generate — EB → compute.tf; emits aws_elastic_beanstalk_application + aws_elastic_beanstalk_environment with the instance profile, LoadBalancerType=application (EB's default LB is Classic), a data-source-resolved solution stack, and VPC placement (private+ALB for LoadBalanced, public subnet for SingleInstance). The migration script deploys app source with the Dockerfile (EB builds the image at deploy time — no ECR push); Terraform provisions, the CLI deploys.

Discover — captures the App Engine version fields (service, runtime, instance_class, scaling, serving_status, project, env_variables) the mapping needs.

Scope: this spans discover→clarify→design→estimate→generate — it's one coherent feature (see the updated description for the per-phase rationale). TF-less App Engine (gcloud/app-code-only) remains a #149 follow-up, documented in the "Discovery Inputs" note.

Cross-skill note (heroku-to-aws): heroku's EB path still omits LoadBalancerType (→ Classic LB) and defaults LoadBalanced to 1 instance vs. this PR's 2 — flagging for a possible heroku follow-up so the two skills converge.

Testing: lint:md (0 errors), lint:types, lint:frontmatter, test (57/57), fmt:check, and the bandit/gitleaks scans pass locally; semgrep runs in CI (binary not installed locally). AWS specifics (EB option-setting namespaces, solution-stack format, App Engine Terraform field paths) were verified against the AWS docs and the Terraform Google provider.

@herosjourney

Copy link
Copy Markdown
Contributor

TL;DR requesting one logic fix and a rebase before merge:

Fix: make Q7b respect Q5. Q5 asks "do you need multi-cloud portability?": if yes, the plugin routes all compute to portable EKS. Q7b (new in this PR) asks "what compute model for your App Engine apps?" and defaults to Elastic Beanstalk: the most AWS-locked-in option. The PR never connects them: a user who answered "yes, portable" on Q5 still gets EB by default from Q7b. Fix: when Q5 = multi-cloud, Q7b should not fire (App Engine follows the container path to EKS), same as Q8 already does.

Rebase: GitHub reports conflicts; clarify.md was rewritten (#170/#171) since this branch, so Q7b needs re-checking against the new sheet/batching rules, not just a textual merge.
Three fast-follows (file as issues, don't need to block this PR): (1) add the new compute_model key to the schema-preferences.md catalog, (2) add a compute_model knob to the what-if workshop so users can reprice EB vs Fargate, (3) add a fixture covering the App Engine multi-service fan-out.

azpaulp added 3 commits July 24, 2026 16:27
…k-gcp

# Conflicts:
#	migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md
When Q5 = multi-cloud portability, Q5 already records `compute: "eks"`, but
Q7b still defaulted App Engine → Elastic Beanstalk (the most AWS-locked-in
target). The two signals were never connected — same conflicting-signal class
as the earlier GKE/Q8 issue.

Mirror Q8's existing multi-cloud early-exit for Q7b, end to end:

Clarify:
- Q7b fires only when App Engine present AND Q5 != multi-cloud; N/A otherwise.
- Early-Exit + sheet-correction rules route App Engine → EKS under multi-cloud,
  overriding the EB default. Disposition catalog, defaults table, and answer-
  combination triggers updated to match.

Design:
- fast-path Direct Mapping + PaaS Preferred-Target rows gated on `compute` !=
  "eks"; new enforcement exemption mirrors the compute_model one.
- compute.md rubric: `compute: "eks"` is a criterion-1 hard override (selects
  EKS and stops before the managed-platform/EB branch — fixes first-match-wins
  ordering that would otherwise pick EB/Fargate first). Example 4c added.
- design-infra Pass 1 condition, App Engine fan-out suppression, and Preferred-
  target check updated; app_version resources skipped like any non-EB path
  (no pipeline STOP, no double-warning).
- design-billing path: App Engine → EKS override applies in billing-only mode
  too (compute:eks is a top-level constraint, not a config detail).
- index.md / elastic-beanstalk.md / SKILL.md load-conditions updated.

Verified: fmt:check, lint:md, lint:types, test, lint:frontmatter, shared:check,
fixtures:check, fixtures:assert, gitleaks all green. Adversarial review pass
confirmed routing is STOP-safe and key discipline (compute vs compute_model vs
kubernetes) is clean.
The App Engine billing-heuristic row put the `compute: "eks"` multi-cloud
override note under the column headed "Alternatives (chosen by IaC path)",
which read as if EKS only applied on the IaC path — an LLM reading the row
alone would route a multi-cloud App Engine billing migration to Elastic
Beanstalk. Point the row explicitly at the multi-cloud override prose
directly below the table (which already applies the override on the billing
path), removing the contradiction without changing behavior.
@azpaulp

azpaulp commented Jul 27, 2026

Copy link
Copy Markdown
Author

Thank you @herosjourney! I've pushed an update to address your last comment. Please let me know if there's anything else I can do for this PR. I do intend to issue a follow-up to address nits and small improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants