Skip to content

Fix redirect-hop links in why-choose-pulumi-over-terraform blog post - #21145

Merged
CamSoper merged 5 commits into
masterfrom
fix/blog-terraform-stacks-redirect
Aug 26, 2026
Merged

Fix redirect-hop links in why-choose-pulumi-over-terraform blog post#21145
CamSoper merged 5 commits into
masterfrom
fix/blog-terraform-stacks-redirect

Conversation

@workprentice

@workprentice workprentice Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

This closes out the last survivor from the /docs/concepts/* redirect-hop cleanup (see card 52cf77c0 and PRs #21072, #21138): links in the Pulumi vs. Terraform blog post that still pointed at old, 301-redirected URLs.

What changed

Stacks links (original scope). Rewrote all three occurrences of https://www.pulumi.com/docs/concepts/stacks/ (lines 33, 49, 147 in content/blog/why-choose-pulumi-over-terraform/index.md) to the direct, live target https://www.pulumi.com/docs/iac/concepts/stacks/.

Resource-options links (folded in from the pre-existing review finding). The same file carried a second class of hop on the lines this PR already touches: aliases, protect, deleteBeforeReplace, and replaceOnChanges all linked to /docs/iac/concepts/options/<name>/, which each 301 to /docs/iac/concepts/resources/options/<name>/ per the target page's own aliases: frontmatter. retainOnDelete on the same lines already used the canonical path, which is what made the others stand out. The reviewer flagged this on lines 33, 46, and 48; while fixing those I found the same hop repeats twice more in the file (L106, L143) that weren't in the original finding. Rewrote 10 links total from the old path to the canonical one; the other 2 occurrences of retainOnDelete already pointed at the canonical path before this push, which is what made the other ten stand out in the first place.

No prose was reflowed anywhere in this PR; every change is a pure link-target substitution.

Verified live immediately before this PR:

  • https://www.pulumi.com/docs/concepts/stacks/ -> 301
  • https://www.pulumi.com/docs/iac/concepts/stacks/ -> 200 (direct, no further hop)
  • https://www.pulumi.com/docs/iac/concepts/options/{aliases,protect,deletebeforereplace,replaceonchanges}/ -> 301 each
  • https://www.pulumi.com/docs/iac/concepts/resources/options/{aliases,protect,deletebeforereplace,replaceonchanges}/ -> 200 each (direct, no further hop)

Why this was deferred, and why it's safe now

This file was excluded from the earlier cleanup passes because two open PRs (#21062, #21066) also touch it, and the working assumption was that editing it risked a merge conflict. That assumption didn't hold up under a direct check:

To be certain rather than just line-counting, I ran git merge-tree --write-tree for this branch against both PR branches (fetched as pull/21062/head and pull/21066/head). Both produced a clean merge tree with no conflict markers or messages:

merge-tree --write-tree fix/blog-terraform-stacks-redirect pr-21062 -> c10b7ef56c07edc372453e0329b7cbcc5fe58fd4 (clean)
merge-tree --write-tree fix/blog-terraform-stacks-redirect pr-21066 -> 613bb5caa8a623a98a5d3404562518a5c957f64a (clean)

Both #21062 and #21066 have been open and untouched for several days, so this fix should not continue to wait on them.

Why this page matters

why-choose-pulumi-over-terraform is the pulumi vs terraform comparison page — our highest-intent Tier 1 keyword (~720/mo) and a page LLM answer engines cite heavily for IaC comparison queries. Redirect hops on it waste crawl budget and dilute link equity on the page where that costs the most.

Verification

  • node ./scripts/lint/lint-markdown.js content/blog/why-choose-pulumi-over-terraform/index.md -> 0 errors
  • Pre-commit hook (prettier + markdown lint) ran clean on commit
  • grep -c '/docs/iac/concepts/options/' index.md before/after: 10 -> 0; grep -c '/docs/iac/concepts/resources/options/' before/after: 2 -> 12

Closes the deferred item tracked on the Marketing Content Calendar board.


🧠 This PR was created by workprentice.

Remaining redirect hops (closed out)

The review's own sweep of this file after the resource-options fix found four more hop families outside that set, all pre-existing in the post before this PR touched it:

Current link Occurrences Canonical target
/docs/iac/adopting-pulumi/import/ 4 /docs/iac/guides/migration/import/
/docs/deployments/deployments/drift/ 2 /docs/deployments/concepts/drift/
/docs/esc/guides/integrate-with-pulumi-iac/ 1 /docs/esc/guides/pulumi-iac/
/docs/iac/adopting-pulumi/migrating-to-pulumi/from-terraform/ 1 /docs/iac/guides/migration/migrating-to-pulumi/from-terraform/

Each mapping was independently re-verified against origin/master's aliases: frontmatter (not taken on the review's word alone), and each new URL was curl-checked live at a direct 200 before editing. Since this PR's stated purpose is removing redirect hops from this file and it was already open here, closing out every remaining hop in one pass beats leaving four more for a future sweep to rediscover. With this push, grep -c '/docs/concepts/\|/docs/iac/concepts/options/\|/docs/iac/adopting-pulumi/\|/docs/deployments/deployments/\|/docs/esc/guides/integrate-with-pulumi-iac/' against the file returns 0.

Rewrite three occurrences of https://www.pulumi.com/docs/concepts/stacks/
(a 301 redirect) to the direct 200 target
https://www.pulumi.com/docs/iac/concepts/stacks/, removing an unnecessary
redirect hop on the Pulumi vs Terraform comparison page.
@CamSoper
CamSoper marked this pull request as draft August 26, 2026 18:57
@CamSoper
CamSoper marked this pull request as ready for review August 26, 2026 18:58
@github-actions github-actions Bot added review:triaging Claude Triage is currently classifying the PR domain:blog PR touches blog posts or customer stories review:in-progress Claude review is currently running and removed review:triaging Claude Triage is currently classifying the PR labels Aug 26, 2026
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-08-26T21:23:10Z

Tip

Summary: acd3315 is the one-byte fix for the only blocker: the trailing newline is back, verified at the byte level rather than taken from the diff marker — the file's last two bytes are now . \n and it is 283 lines, with no extra blank line added at the end. That was the sole MD047 error reported on the previous run, so the lint step should now clear (the Install deps and build site job was still running when this review was written; nothing else in the push can affect it). The push touches nothing but that byte — no prose, links, code, or frontmatter changed — so the redirect-hop work from 10621f0 and the earlier fact-check both carry forward unchanged. Outstanding is back to zero.

Review confidence:

Dimension Level Notes
mechanics HIGH Fix verified at the byte level against 10621f0; CI build still in progress at review time
facts MEDIUM Two claims came back unverifiable on the initial review; both are the post's own qualitative positioning prose rather than checkable specs, and both were checked against pages unrelated to the claim. Unchanged by this push.
coherence HIGH
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 18 of 24 claims verified (2 unverifiable, 0 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 13 inline, 7 Pass 1, 4 Pass 2 (verified 2, contradicted 0, unverifiable 2), 0 Pass 3. Carried forward — this push changes one byte of whitespace and no claim text, so claims were not re-extracted.
  • Cited-claim spot-checks: 3 of 3 cited claims fetched and compared (initial review)
  • Frontmatter sweep: ran on body + meta_desc + social.{bluesky, linkedin, twitter} (initial review; frontmatter untouched since)
  • Temporal-trigger sweep: ran (initial review)
  • Redirect-hop sweep: ran on the previous run over all 49 /docs/… links; carried forward — no link changed in this push
  • Trailing-newline re-check (this run): ran — git show acd3315:content/blog/…/index.md | od -c ends . \n; file is 283 lines, matching the pre-regression shape at 0bf0d7b
  • CI-state check (this run): ran — Install deps and build site is pending on acd3315; the previous failure on 10621f0 was a single MD047 error, now fixed
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: not run (no fenced code blocks changed)
  • Editorial-balance pass: ran (single-subject, N/A)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
0 0 0 3

✏️ 1 one-click style suggestion is posted inline — apply it from the Files changed tab.

🔍 Verification trail

24 claims extracted · 18 verified · 2 unverifiable · 0 contradicted · 1 detector findings
  • L283 in content/blog/why-choose-pulumi-over-terraform/index.md "file ends with a single newline character" → ✅ verified (evidence: at acd3315 the file's final bytes are . \n and it is 283 lines — the same shape it had at 0bf0d7b before the regression; checked with git show acd3315:… | od -c rather than read off the diff's \ No newline at end of file marker. This was the only MD047 error reported by the failing run on 10621f0; source: working tree and object store at acd3315, prior failure at job 98325781933)
  • L33 in content/blog/why-choose-pulumi-over-terraform/index.md "the eight links rewritten by 10621f0 point at canonical pages, not aliases" → ✅ verified (evidence: all four targets exist in the repo, and each declares the old path under aliases:content/docs/iac/guides/migration/import/_index.md L14 lists /docs/iac/adopting-pulumi/import/, content/docs/deployments/concepts/drift.md L7 lists /docs/deployments/deployments/drift/, content/docs/esc/guides/pulumi-iac/_index.md L12 lists /docs/esc/guides/integrate-with-pulumi-iac/, content/docs/iac/guides/migration/migrating-to-pulumi/from-terraform.md L14 lists /docs/iac/adopting-pulumi/migrating-to-pulumi/from-terraform/. A repo-wide sweep finds no page declaring any of the four new paths as an alias, so each resolves directly; source: pulumi/docs content tree at 10621f0)
  • L33 in content/blog/why-choose-pulumi-over-terraform/index.md "the 10 rewritten resource-option links now point at canonical pages, not aliases" → ✅ verified (evidence: content/docs/iac/concepts/resources/options/{aliases,protect,deletebeforereplace,replaceonchanges,retainOnDelete}.md all exist, and a repo-wide sweep of aliases: frontmatter finds no page claiming any /docs/iac/concepts/resources/options/<name>/ path — so each link resolves directly; source: pulumi/docs:content/docs/iac/concepts/resources/options/)
  • L33 in content/blog/why-choose-pulumi-over-terraform/index.md "no /docs/iac/concepts/options/ link remains in the file" → ✅ verified (evidence: grep -n "concepts/options/" content/blog/why-choose-pulumi-over-terraform/index.md at 0bf0d7b returns nothing; the 12 resource-option links now all read concepts/resources/options/ — 10 rewritten by that push, 2 retainOnDelete links already canonical before it; source: working tree at 10621f0)
  • L31 in content/blog/why-choose-pulumi-over-terraform/index.md "Terraform's own module refactoring guidance discusses refactoring tradeoffs (see https://developer.hashicorp.com/terraform/language/modules/develop/refactoring…" → ✅ verified (evidence: The cited page "Refactor modules" exists and covers refactoring guidance and tradeoffs, e.g. "Terraform v1.1 and later is required to use moved blocks to explicitly refactor module addresses. Instead, use the terraform state mv CLI…; source: https://developer.hashicorp.com/terraform/language/modules/develop/refactoring)
  • L33 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi provides a first-class workflow via the 'deleteBeforeReplace' resource option." → ✅ verified (evidence: Pulumi docs confirm deleteBeforeReplace as a real, documented resource option: "Setting the deleteBeforeReplace option to true means that Pulumi will delete the existing resource before creating its replacement." It's implemented…; source: pulumi/docs:content/docs/iac/concepts/resources/options/deletebeforereplace.md)
  • L33 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi does not remove every hard problem in cloud infrastructure but gives teams stronger tools for many day-to-day pain points." → 🤷 unverifiable (evidence: The cited URL is Pulumi's Languages & SDKs landing page, which is a generic marketing/navigation page listing IaC languages, key capabilities, and registry stats — it contains no statement resembling 'Pulumi does not remove every hard…; source: https://www.pulumi.com/docs/iac/languages-sdks/)
  • L46 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi aliases can map old resource identities to new ones during refactors." → ✅ verified (evidence: Pulumi changelog (v0.17.15/v0.17.26) confirms: "Add support for renaming resources via the aliases resource option. Adding aliases allows new resources to match resources from previous deployments which used different names…; source: pulumi/pulumi changelog/v0.17.15.md, v0.17.26.md; docs/references/deployment-schema.md ("A list of previous URNs that this resource may have had in previous deployments"))
  • L46 in content/blog/why-choose-pulumi-over-terraform/index.md "When using Pulumi aliases, aliases must model the old identity correctly." → ✅ verified (evidence: Pulumi SDK source confirms aliases work by identity matching: sdk/go/pulumi/resource.go states "Aliases applies a list of identifiers to find and use existing resources" — meaning the alias must correctly specify the old resource's…; source: gh search code --owner pulumi "Aliases applies a list of identifiers" (sdk/go/pulumi/resource.go); content/blog/why-choose-pulumi-over-terraform/index.md L46)
  • L47 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi tracks secrets and encrypts secret values in state." → ✅ verified (evidence: Pulumi source shows secret values are encrypted using a KMS/passphrase-derived data key and marked as secrets in the stack state, e.g. sdk/go/common/apitype/core.go: "EncryptedKey is the KMS-encrypted ciphertext for the data key used…; source: gh search code --owner pulumi "encryptedKey" (pulumi/pulumi sdk/go/common/apitype/core.go, pkg/secrets/*))
  • L47 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi encrypts secret values in state." → ✅ verified (evidence: Multiple Pulumi docs comparison pages consistently state: "Pulumi treats secrets as a first-class primitive. Values marked as secrets are encrypted in transit and at rest in the state file, anything derived from a secret is also…; source: pulumi/docs:content/docs/iac/comparisons/terraform/_index.md)
  • L47 in content/blog/why-choose-pulumi-over-terraform/index.md "Secrets tracked by Pulumi are still available to code at runtime." → ✅ verified (evidence: Pulumi docs confirm that despite encrypted storage, secret values are decrypted into plain text for use within program code at runtime: "Note that when using apply or Output.all, secrets are decrypted into plain text for use within the…; source: pulumi/docs:content/docs/iac/concepts/secrets/_index.md)
  • L47 in content/blog/why-choose-pulumi-over-terraform/index.md "Sensitive values in Terraform can still require careful state and plan handling." → ➖ not-a-claim — a general, widely-acknowledged characterization of Terraform's handling of sensitive values rather than a specific, falsifiable technical assertion.
  • L48 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi resource options such as protect, retainOnDelete, deleteBeforeReplace, and replaceOnChanges make intent explicit for lifecycle safety." → ✅ verified (evidence: Pulumi's own HCL language reference documents all four options as real resource options with dedicated docs pages: protect ("Mark the resource protected in state..."), retain_on_delete/retainOnDelete ("Keep the cloud resource when…; source: repo:content/docs/iac/languages-sdks/hcl/hcl-language-reference.md)
  • L48 in content/blog/why-choose-pulumi-over-terraform/index.md "Provider behavior and replacement semantics still matter in Pulumi." → ➖ not-a-claim — a general assertion about Pulumi's own design characteristics, not a falsifiable claim citing an external source, number, or specific capability.
  • L49 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi stacks model environments with per-stack config, secrets, history, and outputs." → ✅ verified (evidence: The Pulumi Stacks concepts page confirms stacks are the mechanism for modeling distinct environments, each with independent configuration, secrets, state/history, and outputs. Though the fetched body was truncated before the detailed…; source: https://www.pulumi.com/docs/iac/concepts/stacks/)
  • L50 in content/blog/why-choose-pulumi-over-terraform/index.md "Over-abstracted Pulumi components can become hard to use." → ➖ not-a-claim — the author's own qualitative caveat in a comparison table's "What still needs care" column, not a falsifiable factual assertion.
  • L50 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi components and packages use normal language abstractions for code reuse." → ✅ verified (evidence: Pulumi docs and blog content consistently describe components as built via ComponentResource subclasses using standard language constructs (classes) in TypeScript, Python, Go, etc., enabling code reuse: e.g. glossary.toml: "A logical…; source: pulumi/docs:data/glossary.toml, pulumi/docs:content/blog/pulumi-components/index.md)
  • L51 in content/blog/why-choose-pulumi-over-terraform/index.md "Imported Pulumi code still needs review and cleanup." → ✅ verified (evidence: Pulumi's own import CLI source states: "You will need to copy and paste the generated code into your Pulumi application and manually edit it to correct any errors." Docs also note generated code from import "is flat — every resource at…; source: gh search code --owner pulumi "pulumi import review generated code" (pulumi/pulumi:pkg/cmd/pulumi/operations/import.go; pulumi/docs:content/docs/insights/discovery/discovered-stacks/migrate.md))
  • L51 in content/blog/why-choose-pulumi-over-terraform/index.md "The 'pulumi import' command and migration tooling support gradual adoption from Terraform." → ✅ verified (framing: Source confirms pulumi import + --from terraform conversion tooling exist and are used for state migration, which directly supports the claim's "gradual…; evidence: The pulumi import CLI command (auto-generated docs from pulumi/pulumi source) supports importing existing resources, including via --from to "Invoke a converter to import the resources" (e.g. from Terraform state, as referenced in…; source: repo:content/docs/iac/cli/commands/pulumi_import.md; pulumi/pulumi sdk/nodejs/automation/stack.ts comment "pulumi import --from terraform ./tfstate.json")
  • L52 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi Cloud can serve as a Terraform state backend with encrypted storage, locking, history, and RBAC." → ✅ verified (evidence: Corroborated by pulumi/docs content across multiple pages, e.g. the official announcement: "Pulumi Cloud can serve as a Terraform state backend...gaining the benefits of Pulumi Cloud...encrypted state storage, update history, state…; source: gh search code --owner pulumi "terraform-state-backend" (pulumi/docs:content/blog/terraform-state-backend-pulumi-cloud/index.md, content/docs/iac/comparisons/terraform/_index.md))
  • L52 in content/blog/why-choose-pulumi-over-terraform/index.md "State still needs a clean migration plan as resources move to Pulumi." → ➖ not-a-claim — a general positioning statement authored by the post itself about migration planning, not a falsifiable factual assertion citing an external source.
  • L145 in content/blog/why-choose-pulumi-over-terraform/index.md "Pulumi supports referencing outputs from another stack via pulumi.StackReference, as shown by referencing 'acme/networking/prod'." (also L150) → ✅ verified (evidence: Pulumi SDK defines class StackReference(CustomResource) in sdk/python/lib/pulumi/stack_reference.py (and equivalent TS/Go/.NET implementations), which is the documented mechanism for referencing outputs from another stack by its…; source: gh search code --owner pulumi "class StackReference" --repo pulumi/pulumi)
  • L147 in content/blog/why-choose-pulumi-over-terraform/index.md "Many teams eventually need stronger boundaries than Terraform workspaces provide for configuration, secrets, history, and cross-environment outputs." → 🤷 unverifiable (evidence: The pre-fetched page body is truncated to generic nav/marketing boilerplate and does not contain any passage discussing Pulumi Stacks' advantages over Terraform workspaces for configuration, secrets, history, or cross-environment…; source: https://www.pulumi.com/docs/iac/concepts/stacks/)
  • L33 in content/blog/why-choose-pulumi-over-terraform/index.md "the new /docs/iac/concepts/stacks/ target is canonical, not another alias" → ✅ verified (evidence: content/docs/iac/concepts/stacks.md exists as a real page in the repo, and no page declares /docs/iac/concepts/stacks/ as an alias, so the three rewritten links resolve directly with no redirect hop; source: pulumi/docs:content/docs/iac/concepts/stacks.md)

📊 Editorial balance

Single-subject post; balance check N/A.

🚨 Outstanding in this PR

No outstanding findings in this PR.

⚠️ Low-confidence

No low-confidence findings in this PR.

Style suggestions

Optional polish from pattern-based linting — never blocking, not counted above. Take the ones that read better and ignore the rest. ✏️ marks one you can apply from the Files changed tab — use Add suggestion to batch on each, then Commit suggestions to take several in a single commit.

content/blog/why-choose-pulumi-over-terraform/index.md
  • line 216: [style] wordiness — 'it is' is too wordy. ✏️

📋 Triaged verifier findings

I double-checked these and realized they weren't real findings — click to expand
  • [L33] content/blog/why-choose-pulumi-over-terraform/index.md"Pulumi does not remove every hard problem in cloud infrastructure but gives teams stronger tools for many day-to-day pain points." — verdict: unverifiable. Mis-sourced: checked against the Languages & SDKs landing page, which has nothing to do with the sentence. The sentence is the post's own hedge — a qualitative framing statement, not a spec or capability a reader could look up — so no citation is missing and there is nothing to confirm.

  • [L147] content/blog/why-choose-pulumi-over-terraform/index.md"Many teams eventually need stronger boundaries than Terraform workspaces provide for configuration, secrets, history, and cross-environment outputs." — verdict: unverifiable. Mis-sourced: checked against the Pulumi stacks concepts page, which documents what stacks do but would never assert what "many teams eventually need." The observation about teams is the author's own, and the capability half of the sentence is separately confirmed on the L49 stacks line above.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues surfaced in the touched files.

✅ Resolved since last review

  • [L283] content/blog/why-choose-pulumi-over-terraform/index.mdCI blocker: missing trailing newline (MD047). Resolved in acd3315. Verified the same way the regression was found, at the byte level rather than from the diff marker: git show acd3315:content/blog/why-choose-pulumi-over-terraform/index.md | od -c ends . \n, and the file is 283 lines — identical in shape to 0bf0d7b, so the fix restores exactly one newline and doesn't add a stray blank line at the end. That was the only error in the failing lint run, so nothing else should be blocking; the Install deps and build site job on acd3315 was still pending when this review was written, and this push changes no content that the Hugo build could trip over.

  • [L33] content/blog/why-choose-pulumi-over-terraform/index.mdPre-existing (folded in): the eight remaining redirect hops across four families. Resolved in 10621f0, and the mapping is correct on all four — re-derived from the target pages' own frontmatter rather than taken from the previous review's table: /docs/iac/adopting-pulumi/import//docs/iac/guides/migration/import/ (L33, L51, L226, L261), /docs/deployments/deployments/drift//docs/deployments/concepts/drift/ (L55, L216), /docs/esc/guides/integrate-with-pulumi-iac//docs/esc/guides/pulumi-iac/ (L124), and /docs/iac/adopting-pulumi/migrating-to-pulumi/from-terraform//docs/iac/guides/migration/migrating-to-pulumi/from-terraform/ (L283). Each old path is declared under aliases: on the corresponding canonical page, and none of the four new paths is declared as an alias anywhere, so all eight now resolve in one request.

    Re-swept the whole file to confirm the set is closed: all 49 /docs/… links in the post — including /docs/iac/get-started/terraform/terraform-state-backend/, /docs/iac/cli/commands/pulumi_refresh/, /docs/iac/concepts/resources/providers/, /docs/esc/, and /docs/get-started/ — now back real pages rather than aliases. Zero hops remain, which matches the PR description's grep claim.

  • [L33] content/blog/why-choose-pulumi-over-terraform/index.mdPre-existing (folded in): the four resource-option alias links flagged on L33, plus the repeats in the comparison table and body prose. Resolved in 0bf0d7b, and wider than the original finding: 10 links across L33, L46, L48, L106, and L143 were rewritten from /docs/iac/concepts/options/<name>/ to /docs/iac/concepts/resources/options/<name>/. Re-verified — concepts/options/ no longer appears anywhere in the file, all five target pages exist under content/docs/iac/concepts/resources/options/, and none of the new paths is declared as an alias, so every one resolves directly.

    The mention's own correction here is right and the PR description now matches: 10 links rewritten, 12 total resource-option links in the file, the two retainOnDelete links already canonical. Nothing left open on this.

📜 Review history

  • 2026-08-26T19:08:14Z — Verified the three rewritten stacks links resolve to a real canonical page with no further redirect; no blockers. Two unverifiable claims triaged as verification misfires against unrelated pages; noted four remaining option-link redirect hops on the edited line as optional follow-on. (fed3b15)
  • 2026-08-26T20:56:23Z — Re-reviewed after fix push responding to #update-review from @workprentice[bot]. The optional pre-existing item was folded in and widened; verified complete and moved to ✅ Resolved. Claims were not re-extracted — the push is link-only and changes no prose, code, or frontmatter. Swept every remaining pulumi.com/docs/… link in the post against repo content and aliases: frontmatter, which surfaced eight hops in three other families (import, drift, ESC IaC guide); recorded as a new optional 💡 item with the exact canonical targets. Noted, without treating it as a finding, that the mention's "12 occurrences" is the file's total resource-option link count rather than the number rewritten (10). One new commit reviewed. Outstanding remains 0. (0bf0d7b)
  • 2026-08-26T21:14:00Z — Re-reviewed after fix push responding to #update-review from @workprentice[bot]. One new commit. The 💡 pre-existing hop table was folded in; all four mappings independently re-derived from the target pages' aliases: frontmatter rather than trusted from the previous review, all four correct, and a full-file re-sweep of 49 links confirms the set is closed — moved to ✅ Resolved. The mention's "10 links, not 12" correction is accurate and the PR description now matches the diff; no dispute to adjudicate. Claims were not re-extracted (link-only push, no prose/code/frontmatter change); one claim added covering this run's own hop verification. New blocker found that is not about links: the push dropped the file's trailing newline and make lint is failing CI on MD047 — confirmed at the byte level against 0bf0d7b and against the job log, not inferred from the diff marker. Notably nothing local catches it (*.md is prettier-ignored; the pre-commit hook doesn't check line endings), so it needs a deliberate one-byte fix. Outstanding 0 → 1. (10621f0)
  • 2026-08-26T21:23:10Z — Re-reviewed after automated refresh (auto-refresh gate: the push touched only lines carried by outstanding findings). One new commit, scoped strictly to fix-response — no claim re-extraction and no findings raised on untouched content. The MD047 blocker is fixed and moved to ✅ Resolved, verified byte-level (od -c ends . \n, 283 lines, matching 0bf0d7b) rather than from the diff's \ No newline marker, so the fix is confirmed to restore exactly one newline and not a stray blank line. Checked the one pushed line for regressions: the link on L283 is byte-identical to 10621f0 apart from the newline, and no trailing whitespace was introduced. Install deps and build site was still pending on acd3315 at write time; MD047 was the only error in the prior failing run, so nothing else is expected to block. Outstanding 1 → 0. (acd3315)

  • Refresh this review — comment @claude #update-review. Say what you fixed, or which finding you dispute and why; both work in the same mention.
  • Ask for anything else — comment @claude with no hashtag (questions, one-off fixes). Leaves this review untouched.

Important

Please don't hide, resolve, or delete this comment! It breaks things!

📖 How pre-merge review works — the full lifecycle, short-circuits, and escape hatches.

@github-actions github-actions Bot added review:no-blockers Claude review completed cleanly; outstanding is empty and removed review:in-progress Claude review is currently running labels Aug 26, 2026
…sting on this PR

The review bot's pre-existing finding on this PR noted that lines 33, 46,
and 48 of why-choose-pulumi-over-terraform/index.md carry four more
redirect hops of the same class this PR removes: aliases, protect,
deleteBeforeReplace, and replaceOnChanges all linked to
/docs/iac/concepts/options/<name>/, which each 301 (verified live) to
/docs/iac/concepts/resources/options/<name>/ per the target page's own
aliases: frontmatter. retainOnDelete on the same lines already used the
canonical path, which is what made the others stand out.

While fixing those, found the same hop repeats twice more in this file
(L106, L143) that weren't called out in the original finding. Fixed all
12 occurrences with the same s/options/resources\/options/ substitution,
verified with a diff of before/after occurrence counts (10 hop links,
now 0; 12 canonical links, up from 2).
@github-actions github-actions Bot added review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention and removed review:no-blockers Claude review completed cleanly; outstanding is empty labels Aug 26, 2026
@workprentice

workprentice Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Folded this in — pushed 0bf0d7b. All 12 occurrences of /docs/iac/concepts/options/<name>/ in this file (not just the 3 you flagged on L33/L46/L48 — the same hop repeats at L106 and L143 too) now point at the canonical /docs/iac/concepts/resources/options/<name>/. Verified live with curl: each old path 301s, each new path is a direct 200. PR description updated to cover the widened scope.

@claude #update-review

workprentice Bot pushed a commit that referenced this pull request Aug 26, 2026
…output

Addresses both Outstanding findings and both Low-confidence findings
from the review bot on this PR.

Outstanding 1 -- SCAN_ROOTS omitted archetypes/ and static/, so a stale
/docs/concepts/* link born in a page's own generation scaffold
(archetypes/templates/template/index.md, which every new template page
is generated from) went unseen forever, only to be caught in
content/ after it had already been scaffolded into a real page. Added
archetypes, static, and scripts to SCAN_ROOTS. Including scripts pulled
in one occurrence, scripts/search/rank.js:80 (a historical-href check,
not a live link) -- baselined it rather than dropping the scripts root,
since a baselined known occurrence still ratchets while an unscanned
directory does not. static/ added zero new occurrences (verified: it's
mostly binary images, nothing text-scannable matched). Regenerated the
baseline against master state (not this branch's own commits) so it
describes what master actually has today; PR #21138 already removed the
prior rank.js occurrence via a different code path, so this branch's
own widened scan needed the fresh baseline to stay green.

Outstanding 2 -- --print-baseline printed a flat {path: count} object,
but loadBaseline() reads parsed.files || {}. Regenerating the baseline
the obvious way (redirecting --print-baseline output to the baseline
file) silently produced a baseline that loads as empty, then every
baselined file fails with no hint why. Fixed --print-baseline to emit
the same {_note, files} envelope the reader expects, building the
files object from the sorted violation-map keys directly rather than
using a JSON.stringify replacer keyed off Object.keys() (that reads as
a filter and silently drops any key not already present). Also added
--print-baseline to the Usage: block, which previously only documented
it inside main()'s own comments.

Low-confidence 1 -- implemented: a green run now prints one summary
line instead of ~11 file headers and ~56 occurrence lines. Full detail
only prints when the check actually fails. Verified: a fabricated
violation still prints full file-by-file detail and exits 1; a clean
run prints one line and exits 0.

Low-confidence 2 -- decision (not code): kept the ratchet as a
count-only check rather than switching to line-level tracking, since
the baseline is meant to be short-lived and line tracking isn't worth
the added churn for a file that should be deleted once #21138 and
#21145 merge. Documented the limitation directly in the baseline's own
_note so the next reader isn't surprised: fixing one stale link and
introducing a different one in the same file keeps the count unchanged
and still passes.

Verified end-to-end: self-test passes (9/9 fixtures), make-equivalent
full scan is green with the new summary line, --print-baseline output
round-trips through loadBaseline() as 13 non-empty entries, and a
fabricated /docs/concepts/testing/ link injected into
archetypes/templates/template/index.md (a root only reachable after
this fix) correctly fails the check with exit 1 and full detail, then
cleanly reverts to green.
@pulumi-bot

pulumi-bot commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

@github-actions github-actions Bot added review:in-progress Claude review is currently running and removed review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Review updated on @workprentice[bot]'s request.

@github-actions github-actions Bot added review:no-blockers Claude review completed cleanly; outstanding is empty and removed review:in-progress Claude review is currently running labels Aug 26, 2026
The review's own redirect-hop sweep found eight more hops in this file
outside the resource-options set already fixed, in four families:
/docs/iac/adopting-pulumi/import/ (4x), /docs/deployments/deployments/drift/
(2x), /docs/esc/guides/integrate-with-pulumi-iac/ (1x), and
/docs/iac/adopting-pulumi/migrating-to-pulumi/from-terraform/ (1x). Each
target's aliases: frontmatter was independently re-verified against
origin/master (not just taken on the review's word), and each new canonical
URL curl-checked live at a direct 200 before editing. Since this PR's stated
purpose is removing redirect hops from this specific file, and the file is
already open here, closing out every remaining hop in one pass beats leaving
four more for a future sweep to rediscover.
@github-actions github-actions Bot added review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention and removed review:no-blockers Claude review completed cleanly; outstanding is empty labels Aug 26, 2026
@workprentice

workprentice Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both points from this review pass.

Wording nit: fixed the "12 occurrences" claim in the description — it's 10 links rewritten (the other 2 retainOnDelete occurrences were already canonical, which is what made the other ten stand out).

Remaining hop families: folded in all four (adopting-pulumi/import, deployments/deployments/drift, esc/guides/integrate-with-pulumi-iac, adopting-pulumi/migrating-to-pulumi/from-terraform) in 10621f0. Each alias mapping independently re-verified against origin/master, each new URL curl-checked live at 200. Since this PR's whole purpose is removing redirect hops from this file, closing out the complete remaining set beats leaving four more for a future sweep. PR description updated with the full mapping table.

@claude #update-review

@github-actions github-actions Bot added review:in-progress Claude review is currently running and removed review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention labels Aug 26, 2026

@github-actions github-actions Bot 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.

🧹 Optional style suggestions from the pre-merge review — apply or dismiss; none of them block.


Generated by Claude Code

Infrastructure drifts when something changes outside your IaC tool: a hotfix in the console, another controller, or a manual break-glass change. Pulumi gives you first-class tools to find and fix it instead of leaving it to chance. [`pulumi refresh`](https://www.pulumi.com/docs/iac/cli/commands/pulumi_refresh/) reconciles your state with what is actually running in the cloud, and `pulumi preview --diff` shows what the next update would change.

For teams that want this continuously, Pulumi Cloud adds [scheduled drift detection and remediation](https://www.pulumi.com/docs/deployments/deployments/drift/) that runs on a configurable cadence and can automatically remediate drift when it is detected. That turns finding and correcting out-of-band changes into a managed workflow rather than something a person has to remember to check.
For teams that want this continuously, Pulumi Cloud adds [scheduled drift detection and remediation](https://www.pulumi.com/docs/deployments/concepts/drift/) that runs on a configurable cadence and can automatically remediate drift when it is detected. That turns finding and correcting out-of-band changes into a managed workflow rather than something a person has to remember to check.

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.

[style] wordiness — drops filler; meaning unchanged

Suggested change
For teams that want this continuously, Pulumi Cloud adds [scheduled drift detection and remediation](https://www.pulumi.com/docs/deployments/concepts/drift/) that runs on a configurable cadence and can automatically remediate drift when it is detected. That turns finding and correcting out-of-band changes into a managed workflow rather than something a person has to remember to check.
For teams that want this continuously, Pulumi Cloud adds [scheduled drift detection and remediation](https://www.pulumi.com/docs/deployments/concepts/drift/) that runs on a configurable cadence and can automatically remediate drift when detected. That turns finding and correcting out-of-band changes into a managed workflow rather than something a person has to remember to check.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Review updated on @workprentice[bot]'s request.

@github-actions github-actions Bot added review:outstanding-issues Claude review completed; outstanding has author-actionable findings and removed review:in-progress Claude review is currently running labels Aug 26, 2026
@workprentice

workprentice Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Fixed — one-byte trailing-newline restore in acd3315. Verified at the byte level with od -c that the file now ends in \n, matching what MD047 requires. Confirmed no other content changed.

@claude #update-review

@github-actions github-actions Bot added review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention review:in-progress Claude review is currently running and removed review:outstanding-issues Claude review completed; outstanding has author-actionable findings review:stale New commits since last Claude review; refresh on next ready-transition or @claude mention labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Review auto-refreshed — the latest push only touched lines with outstanding findings.

@github-actions github-actions Bot added review:no-blockers Claude review completed cleanly; outstanding is empty and removed review:in-progress Claude review is currently running labels Aug 26, 2026
@CamSoper
CamSoper merged commit 29e5265 into master Aug 26, 2026
14 checks passed
@CamSoper
CamSoper deleted the fix/blog-terraform-stacks-redirect branch August 26, 2026 21:32
workprentice Bot pushed a commit that referenced this pull request Aug 26, 2026
…output

Addresses both Outstanding findings and both Low-confidence findings
from the review bot on this PR.

Outstanding 1 -- SCAN_ROOTS omitted archetypes/ and static/, so a stale
/docs/concepts/* link born in a page's own generation scaffold
(archetypes/templates/template/index.md, which every new template page
is generated from) went unseen forever, only to be caught in
content/ after it had already been scaffolded into a real page. Added
archetypes, static, and scripts to SCAN_ROOTS. Including scripts pulled
in one occurrence, scripts/search/rank.js:80 (a historical-href check,
not a live link) -- baselined it rather than dropping the scripts root,
since a baselined known occurrence still ratchets while an unscanned
directory does not. static/ added zero new occurrences (verified: it's
mostly binary images, nothing text-scannable matched). Regenerated the
baseline against master state (not this branch's own commits) so it
describes what master actually has today; PR #21138 already removed the
prior rank.js occurrence via a different code path, so this branch's
own widened scan needed the fresh baseline to stay green.

Outstanding 2 -- --print-baseline printed a flat {path: count} object,
but loadBaseline() reads parsed.files || {}. Regenerating the baseline
the obvious way (redirecting --print-baseline output to the baseline
file) silently produced a baseline that loads as empty, then every
baselined file fails with no hint why. Fixed --print-baseline to emit
the same {_note, files} envelope the reader expects, building the
files object from the sorted violation-map keys directly rather than
using a JSON.stringify replacer keyed off Object.keys() (that reads as
a filter and silently drops any key not already present). Also added
--print-baseline to the Usage: block, which previously only documented
it inside main()'s own comments.

Low-confidence 1 -- implemented: a green run now prints one summary
line instead of ~11 file headers and ~56 occurrence lines. Full detail
only prints when the check actually fails. Verified: a fabricated
violation still prints full file-by-file detail and exits 1; a clean
run prints one line and exits 0.

Low-confidence 2 -- decision (not code): kept the ratchet as a
count-only check rather than switching to line-level tracking, since
the baseline is meant to be short-lived and line tracking isn't worth
the added churn for a file that should be deleted once #21138 and
#21145 merge. Documented the limitation directly in the baseline's own
_note so the next reader isn't surprised: fixing one stale link and
introducing a different one in the same file keeps the count unchanged
and still passes.

Verified end-to-end: self-test passes (9/9 fixtures), make-equivalent
full scan is green with the new summary line, --print-baseline output
round-trips through loadBaseline() as 13 non-empty entries, and a
fabricated /docs/concepts/testing/ link injected into
archetypes/templates/template/index.md (a root only reachable after
this fix) correctly fails the check with exit 1 and full detail, then
cleanly reverts to green.
workprentice Bot pushed a commit that referenced this pull request Aug 26, 2026
PR #21138 and PR #21145 (the cleanup pass this guard exists to protect)
both merged today. Every one of the 12 baselined files now has 0 real
occurrences of an internal /docs/concepts/* link on master, so the
57-occurrence allowance list was pure dead weight: a future PR could
have introduced up to that many brand-new stale links per file and the
guard would stay green and silent, which is exactly the regression it
exists to catch.

Rebased onto current master and reset the baseline to {}, updating its
_note and this script's own doc comments to stop describing the two
PRs as still open. Verified against the merged tree: a full scan finds
0 occurrences in all 12 previously-baselined files, and a fresh
--print-baseline confirms an empty {_note, files: {}} envelope is the
correct state.
CamSoper pushed a commit that referenced this pull request Aug 27, 2026
…/options/* redirect hops (#21155)

* Fix internal redirect-hop and broken links in the /docs/using-pulumi/ and /docs/iac/concepts/options/ hop classes

Three /docs/using-pulumi/ link defects, each independently verified:

- debugging-providers.md carried a stray `/docs/using-pulumi/pulumi-packages/`
  alias that collides with the same alias already declared on
  content/docs/iac/concepts/packages/_index.md:14 (the debugging page wins
  the collision, so the link never reached the packages index). Looks like a
  truncated copy-paste amid a run of `.../debugging-provider-packages/`
  aliases on the same page. Deleted the stray entry.
- import/_index.md:501 linked "component package" to
  /docs/using-pulumi/pulumi-packages/#types-of-pulumi-packages, an anchor
  that exists on neither the debugging page nor the packages index (checked
  both). Repointed to the packages concepts page itself, without the
  nonexistent fragment.
- opentofu.md:148 hopped via scripts/redirects/general-broken-links-redirects.txt:7
  to /docs/iac/guides/migration/, whose #conversion anchor does exist
  (confirmed at guides/migration/_index.md:76). Pointed directly at the
  canonical URL.

Also folded in the remaining /docs/iac/concepts/options/* redirect-hop
occurrences outside the blog file already covered by PR #21145: 8 more
files with live body links to the old /docs/iac/concepts/options/<name>/
path, each verified live (301 -> /docs/iac/concepts/resources/options/<name>/,
confirmed via curl) and rewritten to the canonical
/docs/iac/concepts/resources/options/<name>/ path. aliases: frontmatter
entries under both prefixes were left untouched -- they are legitimate
redirect stubs, not violations.

* Add missing trailing slash on two options/parent.md links flagged by review

---------

Co-authored-by: workprentice <257153108+workprentice@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:blog PR touches blog posts or customer stories review:no-blockers Claude review completed cleanly; outstanding is empty

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants