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
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,13 @@

## Inner runs (artifact-only) — mandatory

When re-running Design or Estimate from this file: **rewrite artifacts only**.
Do **not** advance the backbone mid-workshop.

| Allowed | Forbidden |
| ----------------------------------------------------- | ------------------------------------------------------- |
| Overwrite `aws-design.json` / `estimation-infra.json` | Set design/estimate to `in_progress` |
| Soft-validate estimate invariants before snapshot | Emit `HANDOFF_OK` from Design or Estimate |
| Brief chat note that reprice finished | Touch `current_phase` or advance to `generate` |
| Keep `phases.workshop` as `in_progress` | Run Estimate's post-Estimate workshop offer (recursion) |

Leave `phases.design` and `phases.estimate` as `"completed"`. Leave
`current_phase` at `"estimate"` until `workshop-assemble.md`.

Concrete slices:

1. **Design** — run `design.md` / `design-infra.md` enough to rewrite
`aws-design.json` (and siblings if the run uses them). Skip handoff and
phase-status updates.
2. **Estimate** — run `estimate.md` / `estimate-infra.md` enough to rewrite
`estimation-infra.json`. Skip `HANDOFF_OK`, phase-status, and the workshop offer.
Follow the canonical allowed/forbidden contract in
`references/vendored/workshop/workshop-invariants.md` § 3 for every inner
Design/Estimate run. GCP specifics: Design follows `design.md` § Inner
workshop reprice and Estimate follows `estimate.md` § Inner workshop
reprice (both skip state transitions); `phases.design`/`phases.estimate`
stay `"completed"`; `current_phase` stays `"estimate"` until
`workshop-assemble.md`.

## Baseline capture

Expand Down Expand Up @@ -86,22 +73,13 @@ workshop reprice. Chat note after Estimate:

### 6b. Shareable calculator link (best-effort, never blocks)

If the `aws-pricing-calculator` MCP server is available (try `get_server_info`
once; do NOT retry on failure):

1. Prefer the one-shot `build_estimate` (create + add + lint + save): name
`"GCP migration — {scenario label} ({target_region})"`, services from the
scenario's Balanced-tier `estimation-infra.json` breakdown, each with the
scenario's `target_region` — the calculator computes REGIONAL prices
server-side, which the us-east-1 cache cannot. On a structured
needs-field-discovery response, resolve via `get_service_fields` and retry
ONCE; else fall back to `create_estimate` → `add_service` →
`export_estimate`.
2. Store the URL as `estimation_summary.calculator_url` in the manifest.
3. Any failure or unmappable service → `calculator_url: null`, one chat note,
continue. Workshop numbers stay authoritative; the link is a stakeholder
artifact. (This aligns with #49's Estimate-phase calculator integration —
same server, same degradation rules.)
Follow the canonical procedure in
`references/vendored/workshop/workshop-invariants.md` § 6 with
`{SKILL_LABEL}` = "GCP" — probe once, prefer `build_estimate` on the
scenario's Balanced-tier services, store the URL as
`estimation_summary.calculator_url`, null + one chat note on any failure.
(Aligns with #49's Estimate-phase calculator integration — same server,
same degradation rules.)

### 7. Hand back

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
| No BigQuery target knobs | Keep deferred specialist rows; do not invent warehouse targets |
| No agentic outcome override | Do not mutate `ai_constraints.agentic.*` in v1 |

These rules restate the canonical contract in
`references/vendored/workshop/workshop-invariants.md` (vendored from
`skills/shared/workshop/workshop-invariants.md`, kept byte-identical by
`shared:sync`). When this table and that file disagree, the invariants
file wins — fix this table.

## Decline without entering

When Estimate offer **[B] Proceed toward Generate** is chosen, mark
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Vendored shared files — DO NOT EDIT

These files are **synced copies** of the plugin-level canonical source under
`migrate/plugins/migration-to-aws/skills/shared/`. They are vendored into this skill
so the skill folder is **self-contained** — it runs standalone (lifted out, zipped,
or used on its own) without reaching outside its own directory.

**Do not hand-edit anything in this directory.** Edit the canonical source instead,
then re-sync:

```sh
mise run shared:sync # copy canonical -> every skill's references/vendored/
```

CI enforces that these copies are byte-identical to the canonical source
(`mise run shared:check`, wired into `build`). A stale copy fails the build.

| Vendored path | Canonical source |
| --------------------------------------- | ----------------------------------------------------- |
| `dsl/INTERPRETER.md` | `skills/shared/dsl/INTERPRETER.md` |
| `state/phase-status.schema.json` | `skills/shared/state/phase-status.schema.json` |
| `estimate/complexity-tiers.json` | `skills/shared/estimate/complexity-tiers.json` |
| `estimate/estimation-infra.schema.json` | `skills/shared/estimate/estimation-infra.schema.json` |
| `pricing/aws-infra-pricing.json` | `skills/shared/pricing/aws-infra-pricing.json` |
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# What-If Workshop — Cross-Skill Invariants (canonical)

> Canonical contract for the post-Estimate what-if workshop, vendored into each
> skill (`references/vendored/workshop/workshop-invariants.md`) and kept
> byte-identical by `shared:sync`. Skill workshop files (`workshop.md`,
> `workshop-sheet.md`, `workshop-refresh.md`, `workshop-compare.md`,
> `workshop-assemble.md`) own the skill-SPECIFIC parts — knobs, artifact names,
> engine refreshes — and defer to THIS file for every invariant below. When a
> skill file and this file disagree on an invariant, this file wins; fix the
> skill file.
>
> Placeholders: `{INVENTORY}` = the skill's frozen discovery artifact
> (`heroku-resource-inventory.json` / `discovery.json` /
> `gcp-resource-inventory.json`); `{SKILL_LABEL}` = "Heroku" / "Vercel" / "GCP".

## 1. Discovery is frozen

- The workshop NEVER writes `{INVENTORY}`, capture directories, or any
discovery-derived analysis artifact (coupling, preflight, clusters).
- `scenarios/index.json.inventory_fingerprint` = SHA-256 hex of the raw
`{INVENTORY}` bytes (no JSON re-serialization), recorded at baseline capture.
- Every Apply & reprice MUST recompute the fingerprint first and ABORT with
"Inventory changed since baseline. Re-run Discover before workshop reprice."
on any difference.

## 2. Checkpoint state semantics

- The workshop is a checkpoint: it NEVER becomes `current_phase`. Entry sets
`phases.workshop: "in_progress"`; `current_phase` stays at `"estimate"`
until exit/decline.
- Exit to Generate (assembler): `phases.workshop: "completed"`,
`current_phase: "generate"`. Decline at the Estimate offer: same, without
requiring `scenarios/`.
- Warm-start rule: `current_phase == "estimate"` AND
`phases.estimate == "completed"` AND `phases.workshop == "pending"` →
present the workshop offer; NEVER recompute Estimate.
- If Generate (or later) is already `completed`, apply the Estimate re-entry
guard (confirm → reset downstream to pending) before any refresh.

## 3. Inner runs are artifact-only

When the refresh re-runs a backbone phase (Design/Recommend/Estimate) to
reprice:

| Allowed | Forbidden |
| ------------------------------------- | ---------------------------------------------------------------------------------------- |
| Overwrite that phase's artifact(s) | Set the phase to `in_progress`/`completed` (they stay `completed`) |
| Soft-validate before snapshot | Emit `HANDOFF_OK` from the inner phase |
| One brief chat note when done | Touch `current_phase` or advance the backbone |
| Keep `phases.workshop: "in_progress"` | Run the post-Estimate workshop offer (recursion) |
| | Fail on `_check_single_active_phase`-style preconditions because workshop is in progress |

## 4. Scenario store

- Max **5** scenarios. Before evicting, WARN and NAME the victim (id + label);
never delete `baseline_scenario_id` unless the user explicitly resets.
- Working tree == active scenario: the skill's preference/design/estimate
artifacts always match `index.active_scenario_id`.
- Each manifest's `estimation_summary` carries at minimum:
`aws_monthly_premium` / `_balanced` / `_optimized`, `complexity_tier`,
`pricing_source`, `region_note` (nullable), `calculator_url` (nullable).
- `preferences_subset` records ONLY the knob paths that differ from baseline.

## 5. Region honesty

The sheet always shows: region repricing needs live pricing access (awspricing
MCP) for true regional rates; without it, numbers stay on the us-east-1 cache
basis and every affected estimate carries a `region_note`. Never present
cache-based numbers as regional.

## 6. Shareable calculator link (best-effort, never blocks)

After each scenario snapshot, if the `aws-pricing-calculator` MCP server is
available (probe `get_server_info` once; no retry on failure):

1. Prefer one-shot `build_estimate`: name
`"{SKILL_LABEL} migration — {scenario label} ({target_region})"`, services
from the scenario's Balanced-tier estimate breakdown (the PRIMARY outcome's
set where outcomes exist), each with the scenario's target region — the
calculator computes REGIONAL prices server-side, which the cache cannot.
On a structured needs-field-discovery response, resolve via
`get_service_fields` and retry ONCE; else fall back to
`create_estimate` → `add_service` → `export_estimate`.
2. Store the URL as the manifest's `estimation_summary.calculator_url`.
3. Any failure or unmappable service → `calculator_url: null`, one chat note,
continue. Workshop numbers stay authoritative; the link is a complementary
stakeholder artifact. Unconfigured server → silent null.

Compare tables and stakeholder reports render one link line per non-null
`calculator_url`.
Original file line number Diff line number Diff line change
Expand Up @@ -20,66 +20,12 @@ _contributes:

## Step 0: Pricing Mode Selection

### Step 0a: Load Pricing Cache

Read `references/vendored/pricing/aws-infra-pricing.json` (shared AWS infrastructure pricing data). Check the `_meta.last_updated` date:

- If ≤ 30 days old: **Cached prices are the primary source.** No MCP calls needed for services listed in the file. Set `pricing_source: "cached"`.
- If > 30 days old: Infrastructure prices (Fargate, RDS, S3, etc.) remain reliable. Attempt MCP (Step 0b) for services not in the file; use the cached rates as fallback with `pricing_source: "cached_stale"`.

**Region honesty:** Read `preferences.json.global.target_region`. The pricing cache
`_meta.region` is `us-east-1`. When `target_region` differs:

1. Prefer MCP `get_pricing` with that region for services you look up live.
2. When still using cache rates, set
`workshop.region_note` (and surface in the estimate narrative):
`"Rates from us-east-1 cache applied to {target_region} — verify via awspricing MCP for regional deltas."`
3. When regions match, set `workshop.region_note` to `null` / omit.

**Scenario metadata:** If `preferences.workshop.active_scenario_id` is set, carry
it into the cost-engine contribution as `workshop.scenario_id` for the assembler.

**Fargate arch rates:** When a Fargate service has `aws_config.cpu_architecture`
of `ARM64` (or preferences `workshop.cpu_architecture` is `arm64`), use
`fargate.per_vcpu_hour_arm64` / `fargate.per_gb_mem_hour_arm64` when present;
else x86 Fargate rates + warning.

Each service object carries its rates and (where relevant) a `multi_az_handling` key. Look up the rates from there — do not hardcode them. Apply the cost formula from the Per-Service Calculation Formulas table below.

### Step 0b: MCP Availability Check (only if cache stale or service not listed)

Attempt to reach awspricing MCP with **up to 2 retries** (3 total attempts, 10-second timeout per attempt):

1. **Attempt 1**: Call `get_pricing_service_codes()`
2. **If timeout/error after 10s**: Wait 1 second, retry (Attempt 2)
3. **If still fails after 10s**: Wait 2 seconds, retry (Attempt 3)
4. **If all 3 attempts fail**: Use cached prices. Set `pricing_source: "cached_fallback"`.

### Step 0c: Display Pricing Mode to User

**Before any calculation**, surface the pricing status:

- **Cache fresh + all services covered**: "Pricing source: cached (updated [date], ±5-10% accuracy). Live pricing API not required."
- **Cache stale + MCP available**: "Pricing source: live API (awspricing MCP). Cache is stale ([date]) — using real-time pricing."
- **Cache stale + MCP unavailable**: "⚠️ Pricing source: stale cache only (updated [date]). The awspricing MCP server is unreachable. Proceeding with cached pricing; accuracy ±5-10% for infrastructure."
- **Service not in cache + MCP unavailable**: "⚠️ Some services not in pricing cache and MCP unreachable. Those services will show `pricing_source: unavailable` in the estimate."

### Pricing Hierarchy (per-service lookup order)

| Priority | Source | Condition | `pricing_source` value |
| -------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------- |
| 1 | `references/vendored/pricing/aws-infra-pricing.json` | Service found in the pricing file | `"cached"` |
| 2 | MCP API (`get_pricing`) | Service NOT in the file, MCP available | `"live"` |
| 3 | Pricing file after MCP failure | MCP attempted but failed, service IS in file | `"cached_fallback"` |
| 4 | Formula constants / well-known published rate | NOT in file, MCP failed, but this file's own formulas carry the rate (state it verbatim) | `"estimated"` |
| 5 | Unavailable | NOT in file, MCP failed, no formula constant either | `"unavailable"` |

Row 4 is the documented home of the `services_by_source.estimated` bucket the
schema and assembler already carry: a service priced from a rate this file
itself states (never a guessed or remembered number) is `"estimated"`, always
accompanied by a warning naming the rate and its source. Only a service with
no cache entry, no MCP, AND no stated formula rate is `"unavailable"` and
excluded from totals.
Execute `references/vendored/estimate/pricing-mode.md` (the canonical
Step 0, vendored from `skills/shared/estimate/pricing-mode.md` and kept
byte-identical by `shared:sync`) as this step: cache staleness check, MCP
retry ladder, pricing-mode display, and the per-service pricing hierarchy
(including the `estimated` and `unavailable` rungs). Do not restate or
fork that logic here.

For typical Heroku migrations (Elastic Beanstalk, Fargate, RDS, Aurora, ElastiCache, ALB, NAT Gateway, S3, CloudWatch, Secrets Manager, EventBridge, SES, OpenSearch, MQ), ALL prices are in `aws-infra-pricing.json`. Zero MCP calls needed.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,11 @@ _of_phase: workshop

## Inner runs (artifact-only) — mandatory

When this fragment re-runs Design or Estimate, treat them like an `_exec` worker's
WORK slice (`INTERPRETER.md` § `_exec`): **fragments + assembler artifact write
only**. Do **not** advance the backbone mid-workshop.

| Allowed | Forbidden |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Overwrite `aws-design.json` / `estimation-infra.json` | Set `phases.design` / `phases.estimate` to `in_progress` |
| Soft-validate Property-16 / schema before snapshot | Emit `HANDOFF_OK` from Design or Estimate |
| Brief chat note that reprice finished | Touch `.phase-status.json` `current_phase` or advance to `generate` |
| Keep `phases.workshop` as `in_progress` | Run Estimate's post-Estimate workshop offer (recursion) |
| | Evaluate Design/Estimate `_preconditions` that fail on `_check_single_active_phase` because workshop is already `in_progress` |

Leave `phases.design` and `phases.estimate` as `"completed"`. Leave
`current_phase` at `"estimate"` until the user exits workshop via
`workshop-assemble.md`.

Concrete file slices:

1. **Design** — run `design.md` fragments + the **write** portion of
`design-assemble.md`. Skip Completion Handoff Gate `HANDOFF_OK` and Step 8
(phase-status / advance).
2. **Estimate** — run `estimate-cost-engine.md` + the **write + Present Summary**
portions of `estimate-assemble.md`. Skip `HANDOFF_OK`, phase-status update,
deferred-advance logic, and the what-if workshop offer.
Follow `references/vendored/workshop/workshop-invariants.md` § 3 (canonical
allowed/forbidden contract) for every inner Design/Estimate run. Heroku
specifics: leave `phases.design` and `phases.estimate` as `"completed"`;
the inner Estimate skips the post-Estimate workshop offer; `current_phase`
stays `"estimate"` until `workshop-assemble.md`.

## Baseline capture (no Design yet)

Expand Down Expand Up @@ -119,25 +100,10 @@ Execute Estimate per **Inner runs** above. Overwrite

### 6b. Shareable calculator link (best-effort, never blocks)

If the `aws-pricing-calculator` MCP server is available (its tools respond —
try `get_server_info` once; do NOT retry on failure):

1. Prefer the one-shot `build_estimate` (create + add services + lint + save):
name `"Heroku migration — {scenario label} ({target_region})"`, services
from the scenario's Balanced-tier `estimation-infra.json` breakdown, each
with the scenario's `target_region` — the calculator computes REGIONAL
prices server-side, which is exactly what the us-east-1 cache cannot do.
On a structured needs-field-discovery response, resolve via
`get_service_fields` and retry ONCE; else fall back to
`create_estimate` → `add_service` → `export_estimate`.
2. Store the returned URL as `estimation_summary.calculator_url` in the
scenario manifest.
3. Any tool failure or an unmappable service → set `calculator_url: null`,
note the reason once in chat, continue. The workshop's own numbers remain
authoritative; the link is a complementary stakeholder artifact.

If the server is not configured: set `calculator_url: null` silently (the
sheet already explains regional-rate limits).
Follow `references/vendored/workshop/workshop-invariants.md` § 6 with
`{SKILL_LABEL}` = "Heroku" — probe once, prefer `build_estimate` on the
scenario's Balanced-tier services, store the URL as
`estimation_summary.calculator_url`, null + one chat note on any failure.

### 7. Hand back

Expand Down
Loading
Loading