Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions migrate/plugins/migration-to-aws/.mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@
"temporal-docs": {
"type": "http",
"url": "https://temporal.mcp.kapa.ai"
},
"aws-pricing-calculator": {
"command": "npx",
"args": [
"-y",
"sample-aws-pricing-calculator-mcp@latest"
],
"timeout": 120000,
"type": "stdio",
"autoApprove": [
"get_server_info",
"search_services",
"get_service_fields",
"create_estimate",
"add_service",
"validate_estimate",
"build_estimate",
"export_estimate"
]
}
}
}
1 change: 1 addition & 0 deletions migrate/plugins/migration-to-aws/fixtures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Also see:

- `heroku-workshop/` — Heroku what-if workshop seed + arm64 reprice snapshot + `check_expected_workshop.py`
- `vercel-workshop/` — Vercel what-if workshop seed + outcome-B/x86 reprice + `check_expected_workshop.py`

## Migration report reference fixture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"clarify": "completed",
"design": "completed",
"estimate": "in_progress",
"workshop": "pending",
"generate": "pending",
"feedback": "pending"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Heroku what-if workshop fixtures

Canned Estimate → workshop reprice artifacts for the `heroku-to-aws` what-if
workshop checkpoint (`references/phases/workshop/`, `_kind: checkpoint`).
Discovery inventory is frozen; Design + Estimate refresh under `scenarios/`.

| Path | Role |
| ---------------------------- | -------------------------------------------------------------------------- |
| `seed/` | Post-Estimate baseline (x86 EB `t3.small`, single-AZ) before workshop |
| `after-arm64-reprice/` | After Apply & reprice with `workshop.cpu_architecture=arm64` (`t4g.small`) |
| `expected-workshop.json` | Assertions for the asserter |
| `check_expected_workshop.py` | Stdlib checker |

## Assert a run

```bash
python3 check_expected_workshop.py after-arm64-reprice
# or against a live .migration/<id>/ that followed the arm64 sheet path:
python3 check_expected_workshop.py /path/to/.migration/MMDD-HHMM seed
```

## Fresh-agent replay bar (house standard)

Before opening a PR that changes workshop behavior, a **fresh agent** must produce
an after-state from `seed/` by following the phase specs only (no fixture peek):

1. Copy `seed/*` into a scratch `.migration/<id>/` (include `.phase-status.json`
with `workshop: pending`).
2. Agent plays the SA: enter workshop → set CPU architecture `arm64` → Apply &
reprice → Compare → (optional) Exit without Generate.
3. Run `python3 check_expected_workshop.py <scratch_dir> seed` — must **PASS**.

The committed `after-arm64-reprice/` is the reference snapshot for that path.
Record in the PR that the replay was run (agent transcript or CI note). Manual
demo alone is not enough for the house bar.

## SA demo script (manual)

1. Copy `seed/*` into a scratch `.migration/0719-demo/` (include `.phase-status.json`).
2. Invoke heroku-to-aws Estimate handoff / say **Enter what-if workshop**.
3. Confirm baseline capture creates `scenarios/scenario-001*`.
4. On the sheet, set **CPU architecture → arm64**, leave other knobs; **Apply & reprice**.
5. Confirm Design emits EB `t4g.small` + `cpu_architecture: arm64`, Estimate
balanced total changes, `scenario-002` appears, inventory bytes unchanged.
6. **Compare scenarios** — baseline vs scenario-002 (all three cost tiers).
7. Optional second apply: set `data.database_ha` / `global.availability` to
`multi-az`, Apply & reprice → `scenario-003`.
8. **Exit to Generate** — `workshop-assemble` marks workshop completed and sets
`current_phase: generate`; working tree matches the active scenario.

Partner one-liner: _SAs can run a what-if workshop after Estimate: change region,
HA, compute target, or Graviton preference and compare up to 5 priced scenarios
without re-discovery. (Region dollar deltas need awspricing MCP; otherwise rates
stay us-east-1-cache-based.)_
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"migration_id": "0719-workshop",
"last_updated": "2026-07-19T19:05:00Z",
"current_phase": "estimate",
"phases": {
"discover": "completed",
"clarify": "completed",
"design": "completed",
"estimate": "completed",
"workshop": "in_progress",
"generate": "pending",
"feedback": "pending"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"migration_id": "0719-workshop",
"phase": "design",
"services": [
{
"service_id": "eb:acme-web:web",
"source_resource_id": "acme-web:web",
"heroku_app": "acme-web",
"aws_service": "Elastic Beanstalk",
"confidence": "deterministic",
"aws_config": {
"region": "us-east-1",
"platform": "Docker running on 64bit Amazon Linux 2023",
"instance_type": "t4g.small",
"cpu_architecture": "arm64",
"environment_type": "LoadBalanced",
"tier": "WebServer",
"min_instances": 1,
"max_instances": 2,
"process_type": "web",
"deployment_policy": "Rolling"
}
},
{
"service_id": "rds:acme-web:postgres",
"source_resource_id": "acme-web:postgres",
"heroku_app": "acme-web",
"aws_service": "RDS PostgreSQL",
"confidence": "deterministic",
"aws_config": {
"region": "us-east-1",
"instance_class": "db.t4g.medium",
"multi_az": false,
"storage_gb": 64
}
}
],
"warnings": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"phase": "estimate",
"design_source": "aws-design.json",
"timestamp": "2026-07-19T19:05:00Z",
"pricing_source": {
"status": "cached",
"message": "us-east-1 cache"
},
"accuracy_confidence": "+/-5-10%",
"current_costs": {
"source": "unavailable",
"baseline_note": "no billing"
},
"projected_costs": {
"aws_monthly_premium": 120.0,
"aws_monthly_balanced": 88.0,
"aws_monthly_optimized": 78.0,
"aws_annual_optimized": 936.0,
"breakdown": {
"total": {
"premium": 120.0,
"mid": 88.0,
"low": 78.0
},
"Elastic Beanstalk": {
"premium": 60.0,
"mid": 43.0,
"low": 40.0,
"pricing_source": "cached"
},
"RDS PostgreSQL": {
"premium": 60.0,
"mid": 45.0,
"low": 38.0,
"pricing_source": "cached"
}
}
},
"complexity_tier": "small",
"complexity_inputs": {
"service_count": 2
},
"recommendation": {
"path": "migrate_optimized",
"path_label": "Migrate with Optimized pricing scenario",
"migrate_if": [
"want AWS control plane"
],
"stay_if": [
"need Heroku DX indefinitely"
]
},
"workshop": {
"scenario_id": "scenario-002",
"region_note": null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"migration_id": "0719-workshop",
"skill": "heroku-to-aws",
"resources": [
{
"resource_id": "acme-web:web",
"resource_type": "formation",
"heroku_app": "acme-web",
"config": {
"dyno_type": "standard-1x",
"quantity": 2,
"process_type": "web"
}
},
{
"resource_id": "acme-web:postgres",
"resource_type": "addon",
"heroku_app": "acme-web",
"config": {
"addon_service": "heroku-postgresql",
"plan": "standard-0",
"connection_pooling": true
}
}
],
"billing_profile": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"migration_id": "0719-workshop",
"skill": "heroku-to-aws",
"metadata": {
"timestamp": "2026-07-19T19:00:00Z",
"clarify_mode": "fast_path",
"questions_asked": [
"Q1",
"Q3",
"Q12c",
"Q15"
],
"questions_defaulted": [],
"questions_skipped_not_applicable": []
},
"global": {
"target_region": "us-east-1",
"compliance": "none",
"availability": "single-az",
"maintenance_window": "sun:05:00-sun:06:00",
"environment_naming": "acme",
"migration_approach": "full_cutover",
"interim_cutover": false,
"target_exit_date": null,
"ktlo_warning": null,
"fir_intent": null
},
"data": {
"database_ha": "single-az",
"migration_method": "pg_dump_restore",
"estimated_db_size_gb": 20,
"db_size_source": "plan_derived"
},
"operational": {
"container_registry": "ecr",
"containerization_status": "containerized",
"log_retention_days": 30,
"alerting": "cloudwatch",
"cost_optimization": "balanced"
},
"design_constraints": {
"compute_target": {
"default": "elastic_beanstalk",
"overrides": [],
"chosen_by": "system_recommended",
"recommendation": {
"value": "elastic_beanstalk",
"confidence": "high",
"reasons": [
"web formation fits EB default"
]
}
},
"eb_deploy_method": {
"value": "github_actions",
"chosen_by": "default"
}
},
"defaults_applied": [],
"sources": {
"Q1": "user",
"Q3": "user",
"Q12c": "default",
"Q15": "default"
},
"workshop": {
"active": true,
"cpu_architecture": "arm64",
"last_sheet_at": "2026-07-19T19:00:00Z",
"active_scenario_id": "scenario-002"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"baseline_scenario_id": "scenario-001",
"active_scenario_id": "scenario-002",
"max_scenarios": 5,
"inventory_fingerprint": "bbd511b4df40262d61fb96e1f2e47b0c935f90d0ce398dfc8dc0ea6dd52f1bdf",
"scenarios": [
{
"scenario_id": "scenario-001",
"label": "baseline",
"created_at": "2026-07-19T18:30:00Z",
"source": "baseline",
"manifest": "scenarios/scenario-001.json"
},
{
"scenario_id": "scenario-002",
"label": "arm64",
"created_at": "2026-07-19T19:05:00Z",
"source": "workshop",
"manifest": "scenarios/scenario-002.json"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"migration_id": "0719-workshop",
"phase": "design",
"services": [
{
"service_id": "eb:acme-web:web",
"source_resource_id": "acme-web:web",
"heroku_app": "acme-web",
"aws_service": "Elastic Beanstalk",
"confidence": "deterministic",
"aws_config": {
"region": "us-east-1",
"platform": "Docker running on 64bit Amazon Linux 2023",
"instance_type": "t3.small",
"cpu_architecture": "x86_64",
"environment_type": "LoadBalanced",
"tier": "WebServer",
"min_instances": 1,
"max_instances": 2,
"process_type": "web",
"deployment_policy": "Rolling"
}
},
{
"service_id": "rds:acme-web:postgres",
"source_resource_id": "acme-web:postgres",
"heroku_app": "acme-web",
"aws_service": "RDS PostgreSQL",
"confidence": "deterministic",
"aws_config": {
"region": "us-east-1",
"instance_class": "db.t4g.medium",
"multi_az": false,
"storage_gb": 64
}
}
],
"warnings": []
}
Loading
Loading