Shared wiring to converge before an azure-to-aws skill lands
- Dominant language
- Python
- Stars
- 17
- Forks
- 30
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 36
Description
## Shared wiring to converge before an `azure-to-aws` skill lands
We plan to add an `azure-to-aws` migration skill. Before it does, we should make the **AWS-side product and gate shape** consistent across the existing skills (`gcp-to-aws`, `heroku-to-aws`, `llm-to-bedrock`, `agent-advisor`) so a third source-cloud skill copies **one** converged story instead of choosing between divergent ones.
This issue records the shared-prep consensus from a design review. The guiding principle: **the reusable wiring is AWS-side product/gate shape, not source-side input shape.** A new provider triplicates *product behavior* (decide-vs-execute, report contract, Assess handoff) far more expensively than it triplicates `resources[].address`. The existing `skills/shared/` (canonical + vendored, CI-synced) already covers the estimate contract, pricing fallback, workshop, DSL, and phase-status schema; and `tf-best-practices` is already a source-agnostic policy gate. So most of the prep is *finishing convergence on what exists*, not building new abstractions.
### Do before Azure
1. **Land the in-flight AWS-side gate/report/handoff PRs** so Azure copies one story. **Merge in this order** (this is a dependency order, not the PR-number order — several of these edit the same Heroku `generate.md` / `generate-assemble.md` / `SKILL.md` / report-validator files, so the order matters):
1. #291 — Heroku decision gate (decide is default, Generate is opt-in) — decide-default parity with gcp. **Merge first:** it is the foundational flow change and has the widest file overlap (estimate + generate + SKILL + shared state schema); the others refine what it establishes, so they rebase cleanly onto it rather than the reverse.
2. #287 — tf-best-practices policy gate wired into Heroku Generate. Restructures the Generate completion region on top of the decided flow. **On rebase onto #291:** keep #291's `generate.md` CONSENT GUARD (do not revert it), and merge #287's Scope-Boundary "main-window exception" carve-out with #291's into one paragraph rather than leaving two duplicates. They compose fine (consent guard → policy reconcile → read-only gate); it's a merge-conflict to resolve carefully, not a logic conflict.
3. #288 — blocking Heroku report validation. Builds on #287's Generate/assemble structure and shares the report-validator + the `--mode` argument line.
4. #289 — cost-figure cross-check in the report validators. Narrowest overlap (just the report validator); rebases last, most easily.
- #290 — `llm-to-bedrock` accepts Decide-complete as the Assess handoff (not `generate == completed`). **Independent — merge any time**: it touches only `llm-to-bedrock/SKILL.md`, no file shared with the others. (Conceptually it pairs with #291, which makes Heroku emit the decide-complete tuple #290 consumes, but there is no merge conflict either way.)
**Why land these first, in plain terms:** a new skill gets built by copying an existing one. Right now the existing skills don't agree with each other — Heroku's report gate, cost-check, decision flow, and AI handoff each behave a little differently from gcp's until these five PRs land. If we start Azure while they're still open, whoever writes Azure has to pick *which* version to copy, and will inevitably copy some half-finished behavior. Worse, these PRs are actively rewriting the same files Azure would copy from, so Azure would be built on a moving target and need reworking every time one merges. Landing them first freezes a single, correct "this is how a migration skill generates artifacts, validates its report, checks costs, decides, and hands off to the AI path" story — so Azure is a clean copy of one thing instead of a guess between several. It also means the review effort already spent getting these right isn't repeated a third time.
2. **Extract the Finish-Generate / tf-policy-gate touchpoints as a *vendorable fragment*, not just prose.** The two-touchpoint pattern (authoring posture before writing; a main-window policy run + budget-3 retry after) must not be copied from gcp's `generate-artifacts-infra.md` a third time. #287 showed that "copy gcp's generate steps" produces a gate that cannot run under `_exec: rw`. Extract the split — worker writes `not_run` → main window runs the checker + retry → read-only `_assert POLICY_OK`, fail-closed, and **no "re-run Generate to apply fix_hints"** recovery — into a fragment Azure (and Heroku) *vendor* rather than re-derive. Reuse the consent-gated live-CLI discovery *pattern* (Heroku's `az`/Resource-Graph analogue) too — the discovery **behavior** reuses even though the inventory **schema** does not.
**Why this matters, in plain terms:** the way our skills run the Terraform policy check is subtle and easy to get wrong — the worker that writes the files has no shell, so the check has to run later in the main window, and the failure/retry handling has a specific shape. We already learned this the hard way: #287 first got it wrong by copying gcp's steps as prose, and it took three review rounds to fix. If Azure also copies that as prose, it will very likely reintroduce the same bug. Turning the correct pattern into a single shared piece that Azure *includes* (rather than re-types) means the tricky part is written once and can't silently drift or regress. The same logic applies to the "log into the cloud and read live resources with the user's permission" behavior — that's reusable know-how, even though the actual list of resources each cloud returns is different.
3. **Standardize the Assess handoff so Azure's AI path reuses it.** #290 exists because the `llm-to-bedrock ↔ gcp-to-aws` handoff was gcp-only. Azure OpenAI → Bedrock should call the *same* Decide/Design-complete handoff (the `run_mode: decide` + `current_phase: complete` + `DECISION.md` tuple), not a new Discover contract. #291 makes Heroku emit that same tuple; keep it identical for Azure.
**Why this matters, in plain terms:** an AI/LLM migration (e.g. "move my Azure OpenAI calls to Bedrock") reuses the assessment work the migration skill already did — it shouldn't have to re-discover anything. Today that handoff only understands gcp's signals, which is exactly the problem #290 had to fix. If every source cloud invents its own "assessment is done, here's the decision" signal, the AI-rewrite path needs custom wiring for each one. If instead every skill ends its assessment the same way (the same "decided, here's DECISION.md" marker), the AI path just works for Azure with no new code. Standardizing this now means Azure's AI story is free rather than another integration.
4. **Consolidate gcp's private schema copies onto canonical `shared/`** (hygiene, parallel — not Azure's critical path). `gcp-to-aws` predates the canonical `shared/` tree and still carries private prose duplicates of the phase-status / estimation-infra / RI-SP-eligibility schemas. Finish moving gcp onto the vendored canonical source so a third consumer doesn't multiply the drift surface. (`run_mode` was just added to the canonical `phase-status.schema.json` in #291 — continue in that direction.)
**Why this matters, in plain terms:** the same rules (what the state file looks like, what a cost estimate must contain, when a Reserved Instance applies) currently exist in two places — a shared canonical copy, and an older private copy inside gcp that predates the shared one. Two copies of the same rule drift apart over time, and every skill we add multiplies the number of copies that can disagree. Before we add a third source-cloud skill, gcp should read from the one shared copy like the others do, so there's a single source of truth. This is cleanup, not on Azure's critical path — but doing it now keeps a third skill from inheriting or widening the split, and it's the natural moment since #291 just moved one of these rules (`run_mode`) into the shared copy.
### Explicitly NOT doing (deliberate non-goals)
- **A shared `resource-inventory.schema.json` is not a pre-Azure deliverable.** Discovery input is provider-shaped and **nobody downstream inherits a reader** — GCP is a classified dependency-graph, Heroku is a flat app-grouped model that forbids the graph fields, Azure (ARM/Bicep, Resource Graph) would be a third. A thin base envelope is a CI-checkable convention, not the input analogue of `tf-best-practices` (which works because it's a runnable policy engine emitting the same AWS Terraform). If we want one `$id` + an open `provider_detail`, add it opportunistically *while writing Azure's own `schema-discover-azure.md`* — do not block Azure on it.
- **Do not unify the service-mapping layer.** GCP lookup tables vs Heroku's formation/add-on engine vs Azure's App Service / Cosmos / AKS / Azure OpenAI are genuinely different problems. The shared output is already `aws-design.json → estimation-infra.schema.json`; leave mapping per-skill.
- **Do not put `llm-to-bedrock` or `agent-advisor` on an inventory schema.** They are not source-cloud migrators; the AI path needs the Assess *handoff* (item 3), not a Discover contract.
### Suggested order
1. Land the in-flight PRs in the merge order given in item 1 above: **#291 first**, then #287 → #288 → #289 (they rebase onto the decided flow #291 establishes); **#290 any time** (independent). Together they converge the Generate / report / handoff / decide-default story.
2. Extract + vendor the Finish-Generate / tf-policy-gate fragment (item 2).
3. Finish gcp → canonical `shared/` vendoring (item 4).
4. Write `azure-to-aws` Discover against its own `schema-discover-azure.md`; add a thin shared envelope only if the extra `$ref` is cheap at that point.
### What Azure inherits vs authors (for reference)
- **Inherits unchanged** (via vendored `shared/`): `INTERPRETER.md`, `phase-status.schema.json` (incl. `run_mode`), `estimation-infra.schema.json` (source-agnostic — `current_costs.source: "azure"`), `complexity-tiers.json`, `ri-sp-eligibility.md`, `pricing/aws-infra-pricing.json`, `workshop-invariants.md`.
- **Consumes unchanged** (black box): `tf-best-practices` (authoring posture + policy gate) via the two touchpoints.
- **Authors net-new**: the Discover adapter + `azure-resource-inventory.json` schema, the service-mapping layer, Clarify/Design/Estimate provider specifics, an Azure source-spend pricing cache, `SKILL.md` + vendored README + a `shared:sync` entry.
_Filed as a planning/tracking issue; happy to split into per-item issues if preferred._
Contributor guide
Research direction
Start by reviewing the merge order for #291, #287, #288, #289, and #290, then compare the existing skills with the canonical skills/shared/ tree. Trace the Heroku generate.md, generate-assemble.md, SKILL.md, report-validator files, and gcp's private schema copies. Done means the AWS-side flow is converged, the policy-gate pattern is vendored, and gcp uses canonical shared schemas before Azure work begins.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, azure, gcp, terraform
- Domain
- cloud, devops, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100