Skip to content

feat(gcp-to-aws): post-Estimate Decision gate — Generate becomes opt-in - #185

Open
herosjourney wants to merge 13 commits into
awslabs:mainfrom
herosjourney:feat/decision-gate
Open

feat(gcp-to-aws): post-Estimate Decision gate — Generate becomes opt-in#185
herosjourney wants to merge 13 commits into
awslabs:mainfrom
herosjourney:feat/decision-gate

Conversation

@herosjourney

Copy link
Copy Markdown
Contributor

Summary

The problem, in plain English: Most startups come to this plugin with one question — "should we migrate, and roughly what would it cost?" — but the pipeline treats that as a waypoint on the road to Terraform. The verdict already exists at the end of Estimate (recommendation.outcome, since #173), yet the flow's default motion is onward: workshop offer, feedback prompt, then Generate writes 20 Terraform files and a fill-in TODO list for a user who never said yes to executing. The decision is the product for the startup ICP; execution artifacts are a second product that today isn't opt-in.

What this PR does: makes the post-Estimate fork the product. One pipeline, two exit ramps — no mode picker at the door, no new phase status, no second report engine.

Changes:

  • Decision gate (estimate.md): the workshop offer becomes a three-option gate presented after outer-Estimate HANDOFF_OK: [A] Done for now (decision-complete), [B] Explore what-ifs (existing workshop, benefit-first wording and the data-justified scenario hint preserved), [C] Generate Terraform and migration scripts. The gate leads with the decision pack — verdict (outcome_label), Balanced-tier AWS estimate beside the GCP baseline with its baseline-quality label and the feat(gcp-to-aws): Design summary card + cost baseline-quality labels #175 not-comparable rule, a timeline band from migration-complexity.md, and deferred-specialist items. A/B/C never auto-resolves.
  • Generate is opt-in (SKILL.md hard rule + state machine): generate.md loads only on gate choice C, an accepted resume offer, or an explicit ask for Terraform/scripts. The generate state-machine row now requires that consent; a new decide-complete row (current_phase: "complete" + run_mode: "decide" + generate: "pending") defines resume behavior — offer Execute, never re-run Estimate, never auto-run Generate. Ordered (no-current_phase) evaluation respects the same consent.
  • Workshop returns to the gate (workshop-assemble.md, workshop.md, workshop-sheet.md): exiting the workshop re-presents the gate (A/C, verdict lines refreshed from the active scenario) instead of advancing to Generate. The canonical workshop-invariants.md exit rule is generalized — "advances per the skill's post-Estimate flow; generate by default, or the skill's decision gate when one is defined" — so heroku/vercel behavior is unchanged; vendored trees resynced via shared:sync.
  • Feedback re-anchored (SKILL.md step 8): the post-Estimate feedback checkpoint now fires after the gate resolves, and the gate owns the post-Estimate sequence (no prompt stacking). Side benefit: feat(gcp-to-aws): benefit-first workshop offer, outcome flips in compare, feedback decision check #176's "could you make your migrate/stay decision?" questions now land immediately after the user actually made one.
  • run_mode (schema-phase-status.md): optional field, "decide" | "decide_and_execute", set only by the gate (or resume acceptance) — durable cross-session evidence of Generate consent. Deliberately no "skipped" status: generate stays "pending", and decide-complete is documented as terminal-unless-asked, not failure. Flow state, not a design constraint — never written to preferences.json.

What this deliberately does NOT do (v1 scope, agreed in design review)

Test plan

  • dprint check — clean
  • mise run lint:md — 0 errors
  • mise run lint:frontmatter — OK (both skills)
  • shared:sync + vendored check — OK (3 vendored trees; canonical edit synced, no hand-edited vendored files)
  • fixtures:assert — PASS (6 asserters incl. both workshop goldens)
  • Inner workshop reprice path untouched (no HANDOFF_OK, no status writes — diff-verified)
  • AI-only / billing-only routes get the gate without option B
  • Manual: full run to Estimate → gate appears with verdict/cost/timeline lines → A ends with decide-complete state and resume offers Execute without re-running Estimate
  • Manual: workshop entry → exit → gate re-presents with active-scenario numbers → C proceeds to Generate with run_mode: decide_and_execute

Follow-ups (tracked)

Type of Change

  • Enhancement to existing content

Team Folder

  • migrate/

… product, Generate is opt-in

- estimate.md: workshop offer becomes a 3-option Decision gate (done for
  now / what-ifs / generate). Verdict, cost (with baseline-quality label),
  timeline band, and deferred items presented from existing estimate
  artifacts. Option A ends the run decision-complete; C opts into Generate.
- SKILL.md: 'Generate is opt-in' hard rule (gate C, resume acceptance, or
  explicit Terraform request — never auto-chain); generate state-machine row
  now requires consent; new decide-complete row with resume offer; ordered
  evaluation respects consent; feedback checkpoint re-anchored to fire after
  gate resolution (decision-check lands right after the actual decision).
- workshop: exits return to the Decision gate, not Generate
  (workshop-assemble, workshop.md branch + decline, sheet action label);
  canonical workshop-invariants exit rule generalized (skill may define a
  post-Estimate gate; heroku/vercel behavior unchanged) + vendored resync.
- schema-phase-status.md: optional run_mode field (decide |
  decide_and_execute); decide-complete documented as terminal-unless-asked
  (generate stays pending — no 'skipped' status).
@herosjourney
herosjourney requested a review from a team as a code owner July 24, 2026 05:21
1. State-machine workshop row: 'workshop offer' -> Decision gate (stale
   wording would regress agents to the old Enter/Proceed prompt)
2. Estimate scope boundary: carve out the gate's one-line timeline band;
   full schedules stay Generate-only
3. Every Execute path (gate C, resume Yes, explicit ask) sets
   run_mode: decide_and_execute BEFORE loading generate.md, so a death
   mid-Generate resumes as an Execute run
4. Consent guard at the top of generate.md — refuses and re-presents the
   gate when loaded cold without consent
5. Canonical invariants exit rule tightened: skills with a gate NEVER
   auto-advance to Generate (vendored resynced)
6. handoff-gates.md: decide-complete documented as a valid terminal state,
   not a failure to repair
7. Soft: after gate A, feedback folds into the closing line as a one-line
   Yes/No instead of a separate phase-like prompt
@herosjourney

Copy link
Copy Markdown
Contributor Author

Review fixes landed in 28f46ef — all six improvements plus the soft suggestion:

  1. State-machine workshop row now says "present the Decision gate from estimate.md" — agreed this was the most regression-prone leftover; good catch.
  2. Estimate scope boundary carves out the gate's one-line timeline band explicitly; full schedules/runbooks remain Generate-only.
  3. All Execute paths set run_mode: "decide_and_execute" before loading generate.md — including the explicit-ask path, so a session dying mid-Generate resumes as an Execute run.
  4. Consent guard at the top of generate.md: loaded cold without consent → refuse, re-present the gate; loaded on an explicit ask → set run_mode, proceed.
  5. Canonical invariants tightened to "skills that DEFINE a decision gate re-present that gate — never auto-advance to Generate"; vendored trees resynced (shared:check green, 3 trees).
  6. handoff-gates.md documents decide-complete as a valid terminal state — "not an incomplete run to repair; do not 'fix' it by advancing to Generate."
  7. Feedback after gate A folds into the closing line as a one-line "Quick feedback before you go? [Yes] [No]" — separate prompt kept only for the C path where the user is continuing anyway.

Re-verified after the changes: dprint, markdownlint (0 errors), frontmatter validator, vendored-shared check, and fixtures:assert (6 asserters incl. both workshop goldens) all pass.

On the chat-only decision pack UX hole: agreed and holding the line — PR 2 (shared decision core + durable decision-report.html) is queued, nothing report-shaped added here.

@leon1418 leon1418 left a comment

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.

[🤖 AI review 🤖]

Reviewed all 11 changed files end-to-end against mainline and sibling PRs #180#186.

Design: Sound — the core insight ("the decision is the product; Terraform is a second product") is well-executed as a single-pipeline two-exit-ramp model. run_mode as durable cross-session consent, decide-complete as a terminal-unless-asked state, and the consent guard at the top of generate.md form a defence-in-depth against auto-chaining. State machine, schema, invariants, and handoff-gates all mutually consistent.

Functionality: Workshop → gate return, gate choice handling, resume offers, feedback re-anchoring, and ordered evaluation logic all trace correctly. Vendored trees are byte-identical (verified). The AI-only/billing-only path correctly omits option B.

No blocking findings. Two nits below — both are about documentation clarity rather than correctness. The existing mickylindsay comment (line 141, heading parenthetical noise) is a fair point worth addressing but is not a code-health regression.

Recommend merge once the open reviewer thread is resolved.

Comment thread migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md
Comment thread migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md Outdated
Comment thread migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md Outdated
herosjourney and others added 2 commits August 13, 2026 07:19
…ing, legacy resume

- Drop changelog parenthetical from the Decision gate heading
- Align generate-row + HARD RULE wording on explicit produce-request intent
- Add legacy-generate back-compat for current_phase=generate with no run_mode
  (present resume offer instead of hanging with no matching row)
- Phase Status Update Protocol: current_phase may become complete after
  Estimate when gate A sets run_mode=decide

Co-authored-by: Cursor <cursoragent@cursor.com>
@herosjourney

Copy link
Copy Markdown
Contributor Author

Validity check + fixes

Assessed each open thread before changing code:

Thread Valid? Action
mickylindsay / leon1418 — heading “(replaces auto-advance…)” Yes — changelog noise in a live agent-facing heading Removed; heading is now ### Outer Estimate — Decision gate
leon1418 — looseness of “explicitly asked for Terraform/scripts” Yes — table was weaker than the HARD RULE and could match mere mentions Table + HARD RULE now require a current-turn explicit request to produce Terraform/migration scripts
az-zhu — current_phase: generate + absent run_mode hangs Yes — real gap for in-flight/old auto-advance runs; no row matched Added legacy-generate state-machine row + mandatory resume step: present the decide-complete resume offer (Yes → set run_mode: decide_and_execute and continue; No → decide-complete). Chose re-confirm over silent grandfathering so opt-in semantics stay intact
az-zhu — Update Protocol “complete after generate” only Yes — contradicts gate A setting complete after Estimate Protocol step 4 now documents gate-A complete + run_mode: decide

No disagreements — all four were correctness/clarity issues worth landing before merge.

@leon1418

Copy link
Copy Markdown
Contributor

Merge-readiness check

Verified bc46ccd against all five open review threads:

  • Heading changelog parenthetical (mickylindsay + AI nit) — dropped
  • Generate-row natural-language consent clause (AI nit) — tightened to "current-turn message is an explicit request to produce Terraform/migration scripts", HARD RULE aligned ("not merely mentioning Terraform")
  • In-flight run at current_phase: generate with no run_mode matching no row (az-zhu) — new legacy-generate back-compat row + mandatory step 3 in the resume protocol
  • Update-protocol contradiction with the new terminal state (az-zhu) — step 4 now covers "complete after Estimate when gate A sets run_mode: decide"

All five threads look resolvable as-is.

One remaining blocker — the failing build check is drift:check (job): #206 landed after this branch's last review and the advisor plugin now vendors twins of every file this PR touches. All 11 are flagged (e.g. advisor SKILL.md still has auto-advance, zero run_mode/Decision-gate content). Per the tool's own message: re-copy + re-apply prefix rewrites. One mechanical sync commit unblocks this.

#186 contains these commits and inherits the same sync requirement — this PR should land first.

…isor's vendored copies

Mechanical port of this branch's 11 changed files to their advisor
twins (re-copy + the prefix/path/$id rewrites cross-plugin-drift.ts
normalizes), so the advisor plugin ships the same post-Estimate
Decision gate instead of the old auto-advance.

Verified locally: cross-plugin-drift.ts OK (250 identical, 25
allowlisted), sync-vendored-shared.ts OK on both plugins.

Unblocks the drift:check failure on awslabs#185; changes are herosjourney's,
this commit only mirrors them.
@leon1418
leon1418 requested a review from a team as a code owner August 13, 2026 17:13
@leon1418

leon1418 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Pushed a307579 to unblock drift:check — a mechanical sync of this branch's 11 changed files into their advisor twins (re-copy + the prefix/path/$id rewrites the tool normalizes). No changes to your content; verified locally that cross-plugin-drift and both plugins' vendored-shared checks pass. @herosjourney shout if you'd rather do this differently.

leon1418 and others added 3 commits August 13, 2026 12:59
…th SKILL.md copies

The legacy-generate row widened the table; dprint wants the columns
re-padded. Formatting-only — cross-plugin drift and both vendored-shared
checks re-verified green after formatting.
leon1418 pushed a commit to herosjourney/startups that referenced this pull request Aug 13, 2026
…dvisor's vendored copies

This branch predates awslabs#206's consolidation, so drift:check fails: 16
skill files (report-decision-core.md missing outright, 15 drifted —
SKILL.md, design, discover-preview, estimate, feedback, five generate
phase files, migration-complexity, schema-estimate-infra,
validate-migration-report) carried the decision-report changes on the
migrate side only. Synced via re-copy + the prefix/path/$id rewrites
the tool normalizes.

Also synced what drift:check cannot see — the advisor plugin's OWN
copies outside skills/: scripts/validate-migration-report.py (decision
mode: executive sections + CTA, appendices forbidden),
tests/test_validate_migration_report.py, tools/run-asserters.py (the
gcp-decision-gate asserter mapping), the updated reference fixtures,
and the new fixtures/gcp-decision-gate/ tree. Without these the advisor
plugin would validate decision reports with the pre-decision rules.

The branch also merges feat/decision-gate (awslabs#185's current head — this
PR stacks on it and CI needs its 11-file advisor sync) and main.

Both suites 51/51, both asserter sets PASS, drift 252 identical,
vendored-shared and dprint green.
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