Skip to content

WIP: Custom support-request form at /support/new with stubbed /api/support backend - #21030

Open
CamSoper wants to merge 2 commits into
masterfrom
claude/support-form-plan-jxc143
Open

WIP: Custom support-request form at /support/new with stubbed /api/support backend#21030
CamSoper wants to merge 2 commits into
masterfrom
claude/support-form-plan-jxc143

Conversation

@CamSoper

Copy link
Copy Markdown
Contributor

Proposed changes

WIP — do not merge. First cut of the custom support form for the Zendesk → Intercom migration (context). Intercom has no prebuilt request form, so this builds our own, with the Intercom integration stubbed until the API spec is available.

Form page — /support/new/ (content/support/new/_index.md + layouts/page/support-new.html)

  • Hand-built <form> mirroring the Zendesk field set (email, full name, company, Pulumi org, "I need help with:" select, subject, description, pulumi about output, attachments), styled entirely with the shared .form-* / .btn / .card primitives — no new CSS. All copy lives in front matter; two-card layout (form → confirmation) modeled on /extend-trial.
  • theme/src/ts/support-form.ts (registered in main.ts): per-field validation mirroring the server rules (aria-invalid + .form-error, focus-first-invalid, clear-on-input), org-name normalization (pasted https://app.pulumi.com/<org> URLs reduce to the org name), sessionStorage draft persistence so a failed submit never loses the description, a character counter, ?category=/?subject= prefill, a honeypot, and a graceful error banner for environments where the endpoint doesn't exist (PR previews, local make serve).
  • block_external_search_index: true for now; the /support/ Zendesk redirect and existing support links are untouched until cutover.

Backend — POST /api/support (infrastructure/supportForm.ts + infrastructure/support-form/)

  • A Lambda (Function URL) mounted same-origin through a new CloudFront origin + /api/support* behavior on the existing distribution — no CORS, and traffic stays behind the site's WAF rate limiting. The Function URL is AuthType NONE but rejects any request lacking the x-origin-verify shared secret that only CloudFront injects (secret generated by @pulumi/random, delivered via Lambda env var; comma-separated rotation supported).
  • Handler checks: origin secret → method → content type → 256 KB body cap → JSON parse → honeypot → field validation, with field-level 422 {fields} errors the frontend maps back onto inputs. All responses are Cache-Control: no-store (new api-response-headers policy; the API behavior also drops the edge-redirect Lambda association and markdown-negotiation functions that baseCacheBehavior would otherwise attach).
  • Intercom is stubbed: accepted submissions are logged as structured JSON (support_request_accepted) to a pre-created CloudWatch log group with 90-day retention (submissions contain PII), observable via aws logs tail /aws/lambda/$(pulumi stack output supportFormFunctionName). A TODO(intercom) marks where the ticket-create call goes; the API key will arrive as a stack secret / ESC entry, never in this repo. Attachments currently submit metadata only (filename/size/type) — byte upload is deferred until the Intercom spec defines where files go.
  • Gated by new enableSupportForm stack config, on for www-testing and www-production; dev stacks and PR previews are unaffected (the form degrades to the error banner).
  • Validation is a pure, dependency-free module with unit tests: cd infrastructure && yarn test-support-form (24 tests, passing).

Verified: make lint clean, make build renders /support/new/ with the full form, webpack bundle compiles, infra tsc clean, unit tests pass.

Follow-ups (not in this PR): Intercom ticket call + attachment upload, link cutover (help-links.html, hand-raise-section.html, footer, /contact/ quick links, the /support/ redirect), optional OAC hardening of the Function URL, post-deploy health check for the endpoint.

Unreleased product version (optional)

n/a

Related issues (optional)

Internal Slack thread: https://pulumi.slack.com/archives/D0BRR746EAD/p1787266575569169

🤖 Generated with Claude Code

https://claude.ai/code/session_01DtyVE5R4oYsgzBU7x1AC45


Generated by Claude Code

…port backend

As support moves from Zendesk to Intercom, Intercom provides no prebuilt
request form, so this adds our own:

- /support/new/: a hand-built form (fields mirroring the Zendesk form)
  rendered by layouts/page/support-new.html from front matter, using the
  shared .form-* design system. Client-side validation, org-name
  normalization, sessionStorage draft persistence, char counter,
  query-param prefill, and a honeypot live in theme/src/ts/support-form.ts.
- /api/support: a Lambda (Function URL) mounted same-origin via a new
  CloudFront origin + /api/support* behavior in infrastructure/index.ts,
  gated by the enableSupportForm stack config (on for www-testing and
  www-production). The Function URL is sealed by an x-origin-verify
  shared secret injected by CloudFront.
- The handler validates submissions server-side
  (infrastructure/support-form/validation.ts, unit-tested via
  yarn test-support-form) and stubs the Intercom integration by logging
  accepted entries as structured JSON to CloudWatch (90-day retention).
  The Intercom ticket call and attachment upload land once the API spec
  is available; attachments currently submit metadata only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtyVE5R4oYsgzBU7x1AC45
@github-actions github-actions Bot added the review:triaging Claude Triage is currently classifying the PR label Aug 21, 2026
@CamSoper CamSoper added do-not-merge and removed review:triaging Claude Triage is currently classifying the PR labels Aug 21, 2026 — with Claude
@github-actions github-actions Bot added domain:website PR touches marketing, pricing, legal, or competitive landing pages domain:mixed PR touches more than one domain domain:infra PR touches workflows, scripts, infra, Makefile, or build config review:in-progress Claude review is currently running labels Aug 21, 2026

@unblocked unblocked Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No issues found

About Unblocked

Unblocked has been set up to automatically review your team's pull requests to identify genuine bugs and issues.

📖 Documentation — Learn more in our docs.

💬 Ask questions — Mention @unblocked to request a review or summary, or ask follow-up questions.

👍 Give feedback — React to comments with 👍 or 👎 to help us improve.

⚙️ Customize — Adjust settings in your preferences.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-08-21T15:35:11Z

Tip

Summary: This PR adds a hand-built support-request form at /support/new/ (a new type: page / layout: support-new content page whose copy lives entirely in front matter, in the style of the other form-bearing marketing pages) plus the infrastructure behind it: a /api/support* CloudFront behavior fronting a Lambda Function URL, shared-secret origin verification, payload validation shared in spirit with a client-side mirror, and a stubbed Intercom integration that currently just writes accepted submissions to CloudWatch Logs. Because every string a user reads is front matter, the wrongness that would actually hurt is a bad label or option value in content/support/new/_index.md — a mislabeled category is the first thing a person filing a ticket sees, and the option values are a closed enum the API rejects if they drift. The one such label problem is now fixed (e31402d), leaving only advisory items. Passes run: Hugo build + link integrity (clean; one new page, /support/new/, no alias or URL collisions), frontmatter validation, pattern-based prose linting (no findings), claim extraction and verification across the content and infrastructure files, and a manual read of the layout, the client module, and the Lambda/CloudFront wiring against the existing infrastructure/index.ts conventions.

Review confidence:

Dimension Level Notes
mechanics HIGH Hugo build and frontmatter checks are clean; the page renders and the layout's referenced primitives (card, card-highlight, badge-brand, the shared form classes) all exist.
facts HIGH
coherence MEDIUM The PR is marked WIP with the Intercom backend stubbed, so parts of the flow can't be exercised end-to-end from here.
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 9 of 34 claims verified (0 unverifiable, 0 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 26 Pass 1, 0 Pass 2, 8 Pass 3 (verified 2, contradicted 0, unverifiable 6).
  • Cited-claim spot-checks: not run (no cited claims)
  • Frontmatter sweep: ran on body + meta_desc
  • Temporal-trigger sweep: ran (recency words present in diff; spot-check in-review)
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: not run (no fenced code blocks in content files)
  • Editorial-balance pass: not run (not under content/blog/)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
0 4 0 1

🔍 Verification trail

34 claims extracted · 9 verified · 0 unverifiable · 0 contradicted
  • L2-3 in content/support/new/_index.md "The Pulumi support team will respond to submitted support requests by email." (also L13) → ➖ not-a-claim (evidence: This is a faithful description of the PR author's own product page/workflow: the page itself states "we'll get back to you by email" and "replies come from Pulumi support by email." It's a first-party design statement about Pulumi's own…; source: repo:content/support/new/_index.md)
  • L5 in content/support/new/_index.md "layout: support-new" → ➖ not-a-claim (evidence: "layout: support-new" is a Hugo front-matter field specifying a layout template name, not a falsifiable temporal assertion. The regex-based extractor mistakenly flagged it due to the substring "new".; source: repo:content/support/new/_index.md)
  • L6-8 in content/support/new/_index.md "This support request page is being kept out of search indexing until an Intercom cutover makes it the canonical support entry point." → ➖ not-a-claim (evidence: The line is a verbatim transcription of the file's own front-matter comment: "Transactional form page. Keep it out of search until the Intercom cutover makes it the canonical support entry point." It describes the author's own design…; source: repo:content/support/new/_index.md)
  • L13 in content/support/new/_index.md "Fields marked with an asterisk () on the support request form are required." → ➖ not-a-claim (evidence: The line is the page's own front-matter description of its own support form UI convention ("Fields marked with an asterisk () are required"), authored as part of this same PR's form design — a faithful description of the author's own…; source: repo:content/support/new/_index.md)
  • L25 in content/support/new/_index.md "A user's Pulumi organization name can be found at the URL https://app.pulumi.com/PULUMI_ORG_NAME." → ✅ verified (evidence: The Pulumi Cloud app URL structure places the organization name directly after the domain, e.g. app.pulumi.com/, which is standard and consistently used across Pulumi Cloud (e.g. app.pulumi.com///). The support…; source: repo:content/support/new/_index.md (self-consistent with Pulumi Cloud's known URL scheme app.pulumi.com/{org}/{project}/{stack}))
  • L26-38 in content/support/new/_index.md "The support request form's 'I need help with' category field offers exactly four options: My Pulumi Account/Sales, My Pulumi Program, My Pulumi Cloud, and…" → ✅ verified (evidence: The file's category.options list contains exactly four entries: "My Pulumi Account/Sales" (account-sales), "My Pulumi Program" (program), "My Pulumi Cloud" (cloud), and "Pulumi Documentation/Blog" (docs), matching the claim precisely.; source: repo:content/support/new/_index.md)
  • L45-46 in content/support/new/_index.md "Running pulumi about in the directory containing the Pulumi project prints information about the Pulumi environment that is helpful for debugging." → ✅ verified (evidence: The auto-generated CLI reference for pulumi about states: "Prints out information helpful for debugging the Pulumi CLI. This includes information about: the CLI and how it was built, which OS Pulumi was run from, the current project…; source: repo:content/docs/iac/cli/commands/pulumi_about.md)
  • L49 in content/support/new/_index.md "File contents selected as attachments in the support request form are not actually uploaded; only the file selection is noted, and Pulumi support will ask…" → ➖ not-a-claim (evidence: (escalated from pass3) The claim is a paraphrase of the file's own help text: "Up to 5 files, 20 MB each. File contents aren't uploaded yet: we'll note what you selected and ask for the files by email if we need them." This is the PR…; source: repo:content/support/new/_index.md)
  • L52 in content/support/new/_index.md "Pulumi operates a support ticketing site at https://support.pulumi.com/ where users can open a ticket." → ➖ not-a-claim (evidence: Line 52 is the page's own error_banner copy: "We couldn't send your request just now... please try again in a moment, or open a ticket at https://support.pulumi.com/." This is the PR's own content describing its own fallback UX design…; source: repo:content/support/new/_index.md)
  • L52 in content/support/new/_index.md "Users can open a support ticket at https://support.pulumi.com/ as an alternative if the request form fails to send." → ➖ not-a-claim (evidence: Line 52 is the page's own error_banner copy: "We couldn't send your request just now... please try again in a moment, or open a ticket at https://support.pulumi.com/." This is the PR author's own UI design/fallback text, not a…; source: repo:content/support/new/_index.md)
  • L56 in content/support/new/_index.md "Replies to submitted Pulumi support requests come from Pulumi support via email." → ➖ not-a-claim (evidence: This is the PR's own description of its own support-form workflow ("replies come from Pulumi support by email"), a faithful description of the author's own design/product behavior rather than a third-party-attributed, independently…; source: repo:content/support/new/_index.md)
  • L66 in content/support/new/_index.md "A Pulumi support engineer reviews a submitted support request and replies by email, usually within one business day." → ➖ not-a-claim (evidence: (escalated from pass3) This is the PR author's own stated support-process commitment on a page they are authoring ("A support engineer reviews it and replies by email, usually within one business day."), not a third-party fact…; source: repo:content/support/new/_index.md)
  • L74-75 in content/support/new/_index.md "Users can ask the Pulumi community for help on Slack at https://slack.pulumi.com/." → ✅ verified (evidence: The doc file's help_links section lists: label: Ask the community on Slack with url: https://slack.pulumi.com/, which is Pulumi's well-known public Slack invite link, consistent with the claim that users can join the Pulumi community…; source: repo:content/support/new/_index.md (lines 74-75))
  • L76 in content/support/new/_index.md "- label: Browse the documentation" → ➖ not-a-claim (evidence: This is a UI label string in a data/config block ("label: Browse the documentation") for a support page link, not a falsifiable factual assertion.; source: content/support/new/_index.md L76)
  • L76-77 in content/support/new/_index.md "Pulumi's documentation is browsable at /docs/." → ✅ verified (evidence: The repo contains content/docs/_index.md, a documentation landing page ("title: Documentation... h1: Pulumi Docs") that renders at the /docs/ path, confirming the link target referenced in the support page's help_links section is a…; source: repo:content/docs/_index.md)
  • L78-79 in content/support/new/_index.md "Pulumi publishes service status information at https://status.pulumi.com/." → ✅ verified (evidence: The file itself links to https://status.pulumi.com/ under the label "Check Pulumi service status," and this is Pulumi's well-known public Statuspage-hosted service status domain, consistent with how it's referenced elsewhere in Pulumi's…; source: repo:content/support/new/_index.md (line 79); https://status.pulumi.com/)
  • L80-81 in content/support/new/_index.md "Users can talk to Pulumi sales via the /contact/ page." → 🤝 matches (evidence: content/support/new/_index.md links "Talk to sales" to /contact/, and content/contact/_index.md indeed offers a "Talk to sales" form option (hubspot_form_id for key: sales), confirming the cross-reference target is correct.; source: repo:content/contact/_index.md)
  • L1 in infrastructure/support-form/handler.ts "// Copyright 2016-2026, Pulumi Corporation. All rights reserved." → ➖ not-a-claim (evidence: This is a standard copyright header boilerplate found across Pulumi source files, not a falsifiable factual assertion.; source: infrastructure/support-form/handler.ts)
  • L1 in infrastructure/support-form/validation.test.ts "// Copyright 2016-2026, Pulumi Corporation. All rights reserved." → ➖ not-a-claim (evidence: This is a standard copyright header boilerplate found across Pulumi source files, not a falsifiable assertion.; source: infrastructure/support-form/validation.test.ts)
  • L26 in infrastructure/support-form/validation.test.ts "pulumiAbout: 'CLI Version 3.100.0'," → ➖ not-a-claim (evidence: This is a hardcoded test fixture value (pulumiAbout: "CLI Version 3.100.0") used in a unit test for form validation logic, not an assertion about an actual Pulumi CLI release or requirement.; source: repo:infrastructure/support-form/validation.test.ts)
  • L74-75 in infrastructure/support-form/validation.test.ts "assert.strictEqual(normalizeOrganization('https://app.pulumi.com/example-corp/stacks/dev'), 'example-corp');" → ➖ not-a-claim (evidence: This is a unit test assertion for the PR's own normalizeOrganization function, verifying its own code logic against a sample URL — not a factual claim about an external source.; source: infrastructure/support-form/validation.test.ts)
  • L84 in infrastructure/support-form/validation.test.ts "organization: 'https://app.pulumi.com/example-corp'," → ➖ not-a-claim (evidence: This is a placeholder value in a test file (validation.test.ts) using "example-corp" as a sample organization URL for testing form validation logic. It is not a factual assertion requiring verification.; source: repo:infrastructure/support-form/validation.test.ts)
  • L259 in infrastructure/support-form/validation.test.ts "const response = await supportFormHandler(postEvent({ ...validPayload(), website: 'https://spam.example' }));" → ➖ not-a-claim (evidence: This is a line of test code using a placeholder/example URL ("https://spam.example") as a honeypot field value in a unit test for form validation logic, not a factual or falsifiable claim.; source: infrastructure/support-form/validation.test.ts (reviewed file itself))
  • L1 in infrastructure/support-form/validation.ts "// Copyright 2016-2026, Pulumi Corporation. All rights reserved." → ➖ not-a-claim (evidence: This is a standard copyright header boilerplate found across Pulumi source files, not a falsifiable assertion; it's file/git metadata rather than a factual claim to verify.; source: infrastructure/support-form/validation.ts)
  • L87 in infrastructure/support-form/validation.ts "// Strips a pasted console URL ('https://app.pulumi.com/my-org/...') or" → ➖ not-a-claim (evidence: This is a code comment in the PR author's own file describing their own URL-stripping logic for pasted console URLs; "my-org" is a placeholder example, not a factual assertion about a third party.; source: infrastructure/support-form/validation.ts:L87)
  • L159 in infrastructure/support-form/validation.ts "fields.attachments = 'Each attachment must be 20 MB or smaller.';" → ➖ not-a-claim (evidence: (escalated from pass3) The message string is directly derived from and consistent with the file's own constant: attachmentSizeBytes: 20 * 1024 * 1024 (line 33) equals 20 MB, matching the error text at line 159. This is the PR author's…; source: repo:infrastructure/support-form/validation.ts)
  • L215 in infrastructure/support-form/validation.ts "'Enter just the organization name from https://app.pulumi.com/PULUMI_ORG_NAME ' +" → ➖ not-a-claim (evidence: This is a form validation/help-text string authored by the PR itself, describing its own input format guidance (instructing users to extract the org name from the app.pulumi.com URL, which matches Pulumi Cloud's known URL scheme of…; source: infrastructure/support-form/validation.ts)
  • L1 in infrastructure/supportForm.ts "// Copyright 2016-2026, Pulumi Corporation. All rights reserved." → ➖ not-a-claim (evidence: This is a standard copyright header boilerplate line found across Pulumi repositories, not a falsifiable assertion.; source: infrastructure/supportForm.ts)
  • L48 in infrastructure/supportForm.ts "Version: '2012-10-17'," → ✅ verified (evidence: AWS IAM documentation confirms "2012-10-17" is the current, correct IAM policy language version string: "This is the current version of the policy language, and you should always include a Version element and set it to 2012-10-17."; source: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html)
  • L68 in infrastructure/supportForm.ts "Version: '2012-10-17'," → ✅ verified (evidence: AWS's own IAM documentation confirms "2012-10-17" is the current, correct policy language version: "This is the current version of the policy language, and you should always include a Version element and set it to 2012-10-17."; source: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html)
  • L481 in infrastructure/yarn.lock "resolved 'https://registry.yarnpkg.com/@pulumi/random/-/random-4.21.1.tgz#5bfe67166f530a0bff2ba32fc4abb81ce805308d'" → ➖ not-a-claim (evidence: This is an auto-generated yarn.lock "resolved" URL entry, not a falsifiable editorial assertion. Confirmed corroboration: pulumi/pulumi-random has a real published release "v4.21.1" (tag_name: "v4.21.1"), consistent with the lockfile's…; source: gh api repos/pulumi/pulumi-random/releases/tags/v4.21.1)
  • L42 in layouts/page/support-new.html "support.pulumi.com." → ➖ not-a-claim (evidence: This is a fallback hyperlink in a block pointing to support.pulumi.com, which the same file's header comment confirms is Pulumi's existing Zendesk-based support portal ("the custom support-request form replacing the Zendesk…; source: repo:layouts/page/support-new.html)
  • L146 in theme/src/ts/support-form.ts "return 'Enter just the organization name from https://app.pulumi.com/PULUMI_ORG_NAME (letters, numbers, hyphens, and underscores).';" → ➖ not-a-claim (evidence: This is a UI validation error string describing the PR author's own form logic (the expected URL pattern for a Pulumi org URL), not a falsifiable external claim.; source: repo:theme/src/ts/support-form.ts)
  • L183 in theme/src/ts/support-form.ts "setError('attachments', '${file.name}' is larger than 20 MB.);" → ➖ not-a-claim (evidence: (escalated from pass3) The error message text "is larger than 20 MB" is directly derived from and consistent with the file's own constant const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024; (= 20 MB) defined at line 29 in the same file.…; source: repo:theme/src/ts/support-form.ts)
  • L37 in content/support/new/_index.md "Category option label reads 'Pulumi Documentation/Blog'" → ✅ verified (evidence: re-checked at HEAD e31402d: the fourth option now reads - label: Pulumi Documentation/Blog, matching the product's own name for the docs site (content/docs/_index.md title: Documentation) and this page's own help link on L76 ("Browse the documentation"). value: docs is unchanged and still matches the closed enum CATEGORIES at infrastructure/support-form/validation.ts:15.; source: repo:content/support/new/_index.md L37; repo:infrastructure/support-form/validation.ts L15)
  • L45 in content/support/new/_index.md "Field labels open with 'Please' and name pulumi about as undifferentiated prose" → 🤷 unverifiable (evidence: editorial judgment against STYLE-GUIDE.md and the Google developer documentation style guide's guidance to drop "please" from instructions; the same pattern appears in the description help text on L43.; source: repo:STYLE-GUIDE.md)
  • L26 in infrastructure/support-form/validation.ts "LIMITS.organization: 40 is declared but never read by validateSubmission" → 🤷 unverifiable (evidence: the 40-character bound is enforced only by ORGANIZATION_PATTERN ({0,39} after the leading character); LIMITS.organization appears on no other line of the file.; source: repo:infrastructure/support-form/validation.ts)
  • L6 in infrastructure/package.json "The new test-support-form script is not invoked by any workflow or Makefile target" → 🤷 unverifiable (evidence: no file under .github/workflows/ and no Makefile target references test-support-form or runs yarn scripts in infrastructure/.; source: repo:.github/workflows/; repo:Makefile)
  • L21 in infrastructure/Pulumi.www-production.yaml "enableSupportForm is turned on in production while the ticket backend is still a log stub" → 🤷 unverifiable (evidence: the handler's only sink for accepted submissions is a console.log of the full request body, and infrastructure/supportForm.ts gives the log group retentionInDays: 90.; source: repo:infrastructure/support-form/handler.ts; repo:infrastructure/supportForm.ts)

🚨 Outstanding in this PR

No outstanding findings.

⚠️ Low-confidence

Review each and resolve as appropriate — these don't block the PR.

  • [L45] content/support/new/_index.md — two field strings open with "Please," which the Google developer documentation style guide (our fallback per AGENTS.md) tells us to drop from instructions. L45: "Please run pulumi about in the directory containing the Pulumi project and share the output." → "Run pulumi about in the directory containing your Pulumi project and share the output." L43: "Please enter the details of your request." → "Enter the details of your request." Note that these labels render as plain text, so the backticks above won't format — if you want pulumi about to read as a command rather than a sentence fragment, quote it ("Run "pulumi about" …") or teach the layout to render the label through Markdown. Author's note (2026-08-21): the strings are verbatim from the Zendesk form and the intent was to mirror that copy exactly, so the reword is deferred to Megan's copy review — leaving this open as the marker for that pass.

  • [L26] infrastructure/support-form/validation.tsorganization: 40 is in LIMITS but nothing reads it; the 40-character bound is enforced solely by ORGANIZATION_PATTERN's {0,39}. Two sources of truth for one rule, one of them inert: editing LIMITS.organization would silently change nothing. Either use it (organization.length > LIMITS.organization) or delete the key and note the bound on the pattern. Author's note (2026-08-21): agreed, and folded into the follow-up pass that replaces the Intercom stub with the real call rather than churning the file now.

  • [L6] infrastructure/package.jsontest-support-form is a good suite (262 lines covering validation and every handler status path), but nothing runs it. No workflow and no Makefile target invokes it or any yarn script under infrastructure/, so a regression in validation.ts lands green. Worth wiring into make test or the testing build workflow before this leaves WIP. Author's note (2026-08-21): deliberate for the WIP — the infra program has no CI test step today, and adding one changes shared workflow config that deserves its own review. Reasonable; the finding stays as the reminder that this can't leave WIP without that follow-up landing, since an unrun suite is indistinguishable from no suite.

  • [L21] infrastructure/Pulumi.www-production.yaml — this flips enableSupportForm on in production while the Intercom integration is still a stub, so a merge stands up a live www.pulumi.com/api/support whose only durable record of a submission is a console.log of the whole request body in CloudWatch (90-day retention). The comments acknowledge the PII, and the form explicitly invites pulumi about output and free-form descriptions — the two fields most likely to carry backend URLs, org/stack names, or a pasted token.

    You asked which way reviewers would want this: land it false in production and flip at cutover. block_external_search_index: true plus "nothing links to it yet" are discoverability measures, not access control — the endpoint is a public unauthenticated POST the moment the behavior exists, and anyone who finds /support/new/ gets a form that accepts their request and silently drops it into a log no one is watching. www-testing already exercises the whole path, so the production true buys no coverage; the config flip at cutover is the same one-line change either way, and doing it then means the endpoint and the ticket sink go live together. Not a blocker — the do-not-merge label already holds the line — but it's the safer default if this PR's sequencing changes.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues in touched files.

✅ Resolved since last review

  • [L37] content/support/new/_index.md — the category label typo "Pulumi Documentations/Blog" now reads "Pulumi Documentation/Blog" (resolved in e31402d). Verified at HEAD: value: docs is unchanged, so the closed CATEGORIES enum in infrastructure/support-form/validation.ts, its client-side mirror, and the unit tests are untouched. A repo-wide sweep for "Documentations" (including front matter, the layout, and the client module) returns no remaining occurrences.

📜 Review history

  • 2026-08-21T01:10:44Z — First review: one blocking label typo ("Pulumi Documentations/Blog") on the new support form, plus four advisory notes on prose style, an unused validation limit, unwired tests, and enabling the endpoint in production ahead of the ticket backend (05f210d)
  • 2026-08-21T15:35:11Z — Re-reviewed after fix push (1 new commit, e31402d): the label typo is fixed and verified, value: docs and the API enum unchanged, and a repo-wide sweep finds no other occurrence — moved to ✅ Resolved, leaving 🚨 Outstanding empty. @CamSoper responded to all four ⚠️ items without acting on them; none were disputed on the merits, so all four stay in ⚠️ with the author's disposition recorded inline (Zendesk-verbatim copy deferred to Megan's review; the inert LIMITS.organization folded into the Intercom follow-up; CI wiring deferred as a shared-workflow change). The production enableSupportForm question was answered in the bullet: recommend landing false and flipping at cutover, since www-testing already covers the path and the production flag exposes an unauthenticated endpoint whose only sink is a PII log.

  • 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:outstanding-issues Claude review completed; outstanding has author-actionable findings and removed review:in-progress Claude review is currently running labels Aug 21, 2026
@pulumi-bot

pulumi-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Your site preview for commit e31402d is ready! 🎉

http://www-testing-pulumi-docs-origin-pr-21030-e31402d4.s3-website.us-west-2.amazonaws.com

Changed pages:

@pulumi-bot

pulumi-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Lighthouse Performance Report

Commit: e31402d | Metric definitions

Page Device Score FCP LCP TBT CLS SI
Homepage Mobile 🔴 32 6.3s 14.8s 1325ms 0.001 9.1s
Homepage Desktop 🟡 83 1.1s 2.7s 0ms 0.012 1.1s
Install Pulumi Mobile 🟡 61 5.5s 13.9s 15ms 0.054 5.5s
Install Pulumi Desktop 🟢 92 1.0s 1.6s 0ms 0.023 1.0s
AWS Get Started Mobile 🔴 46 5.3s 8.7s 0ms 0.321 5.3s
AWS Get Started Desktop 🟢 92 1.1s 1.7s 0ms 0.045 1.1s

The fourth "I need help with:" option was carried over verbatim from the
Zendesk form, typo included. The option value (docs) is unchanged, so the
closed enum in infrastructure/support-form/validation.ts, the client-side
validation, and the unit tests are unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtyVE5R4oYsgzBU7x1AC45
@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:outstanding-issues Claude review completed; outstanding has author-actionable findings labels Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

@claude #update-review

Fixed the outstanding finding in e31402d: the category label now reads "Pulumi Documentation/Blog". The label was carried over verbatim from the Zendesk form, typo included; value: docs is unchanged, so the closed enum in infrastructure/support-form/validation.ts, the client-side mirror, and the unit tests are unaffected.

Notes on the four low-confidence findings, none of which I've acted on:

  • "Please" in two field labels — also verbatim from the Zendesk form. Happy to reword to imperative ("Run pulumi about in the directory containing the Pulumi project and share the output") per the Google style guide, but since the intent was to mirror the existing form's copy exactly, I'd rather Megan make that call as part of the copy review.
  • Unused LIMITS.organization — correct that the 40-char bound is enforced only by ORGANIZATION_PATTERN. Worth resolving, but I'd fold it into the same pass that replaces the stub with the real Intercom call rather than churn the file now.
  • test-support-form not wired into CI — fair. It's deliberate for the WIP: the infra program has no CI test step today, and adding one is a change to shared workflow config that deserves its own review rather than riding along here.
  • enableSupportForm: true in production — flagging that this PR is WIP / do-not-merge precisely because of this. The form page is block_external_search_index: true and nothing links to it yet, but if reviewers would rather land the endpoint disabled in production and flip it at cutover, that's a one-line config change and I'll make it.

Generated by Claude Code

@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 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Review updated on @CamSoper'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 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge domain:infra PR touches workflows, scripts, infra, Makefile, or build config domain:mixed PR touches more than one domain domain:website PR touches marketing, pricing, legal, or competitive landing pages 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