Skip to content

Put the hero first and make form failures visible - #684

Merged
gerardrecinto merged 7 commits into
masterfrom
review-site-funnel
Oct 31, 2026
Merged

gerardrecinto merged 7 commits into
masterfrom
review-site-funnel

Conversation

@gerardrecinto

@gerardrecinto gerardrecinto commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Put the homepage pitch and primary demo action before the scroll story on desktop and phones. Start the 6 MB engine after interaction or when its sections approach the viewport, and load the video player only when the visitor presses play.

Inquiry success now requires a server acknowledgement. Error and lost-response states preserve the visitor's answers and explain that delivery is unconfirmed. POSTs are not automatically retried, since a lost response does not prove the first submission failed. Pro requests distinguish an unreachable service, an HTTP error, and a missing checkout link.

Update against current master, retain the light-theme contrast and package documentation corrections, and replace new utility classes with scoped styles so generated CSS stays unchanged. The complete diff is below the 60 KB review limit. The other pages' link-preview metadata is in #693.

Validation: site assembly, CSS reproduction, TypeScript checking, and diff checks pass. Chromium reports 189 passed and 2 skipped. Homepage and accessibility checks passed 129 cases across Chromium, mobile Chrome, and WebKit; the final inquiry and documentation-link regressions passed all 12 cases across those browsers.

Thanks,
Gerard

The hero and its one filled CTA now sit above the scroll story, the wasm preload
is gone and the engine starts on first interaction, and the video is click to
play. Enterprise and Pro forms report real failures instead of claiming success.
Trust text is larger and lighter for contrast, social preview tags gain image
alt text, and fixed test sleeps are replaced with frame and poll waits.

Thanks,
Gerard Recinto
@gerardrecinto gerardrecinto changed the title Put the hero first, make form failures visible, and add a rollback to the site deploy Put the hero first and make form failures visible Oct 29, 2026
@gerardrecinto
gerardrecinto enabled auto-merge (squash) October 29, 2026 07:51
@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini skip

@github-actions

Copy link
Copy Markdown

A skip needs a reason, for example: /gemini skip quota used up until tomorrow

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 567f8129b4bee327a94d6cf987f3455a066e6e49
Verdict: FAIL

  • Security: Server-Side Input Validation for Billing API
    • File: demo/index.html
    • Behavior: The JavaScript functions openEnterpriseCheckoutModal and openProCheckoutModal (via the new postJson helper) send user-controlled data (e.g., name, email, company, message, workspace_name) to the backend billing API endpoints (/api/billing/enterprise-contact, /api/billing/public-checkout). The client-side code does not perform validation or sanitization of these fields before transmission to the server. While escapeHtml is used for client-side display, it does not apply to the data sent in the fetch request body. This means the backend (Go/Java) must rigorously validate and sanitize these inputs to prevent server-side injection vulnerabilities (e.g., SQL injection, command injection if data is used in shell commands) or other processing errors. The lack of client-side validation increases the reliance on robust server-side protection.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini dispute

Security: Server-Side Input Validation for Billing API
The client-side code does not perform validation or sanitization of these fields before transmission to the server.

Observed: postJson sends JSON. The proposed SQL or command injection is unsupported: the finding identifies no interpreting sink or bypass of the server checks.

At reviewed commit 567f8129b4bee327a94d6cf987f3455a066e6e49:

  • handleFormSubmit trims inputs and rejects missing name, email, or company. The HTML also uses required fields and email input types. These are usability checks, not the security boundary.
  • handleSubmitEnterpriseInquiry limits the body, decodes JSON, rate limits, and returns 400 for ErrInvalidInquiry.
  • validateInquiry and SubmitEnterpriseInquiry validate email and bound every field before storage. Caller IDs, status, and timestamps are replaced. PutInquiry stores a typed value in the embedded B-tree, without constructing SQL or executing a command.
  • handlePublicCheckout limits input to 4096 bytes, parses the email, and fixes tenant and tier on the server. Workspace and tier fields from the browser are not bound into this request struct.

The public routes are reachable, but bypassing JavaScript still reaches those server checks. OWASP's guidance requires server validation and distinguishes it from output encoding. HTML-escaping JSON before transmission would alter legitimate free-form data rather than secure an identified sink.

Validation on this commit: go test ./governance ./tools/httpserver -run 'TestSubmitEnterpriseInquiry|TestInquiry|TestHandlePublicCheckout_Requests' -count=1 passes. This covers invalid emails, oversized fields and bodies, and caller-chosen IDs. Browser tests separately cover escaped display and confirmed versus unconfirmed submissions.

Please remove this unsupported blocker and review the current commit against the existing server validation. If a specific reachable sink is still a concern, please identify that path.

Thanks,
Gerard

@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

Thanks,
Gerard

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 567f8129b4bee327a94d6cf987f3455a066e6e49
Verdict: FAIL

  • Correctness Bug (Documentation/Comment): In demo/index.html, the postJson function's comment states: "A network failure, a timeout or a 5xx is tried once more." However, the function's implementation does not include any retry logic and attempts the fetch request only once. The comment should be updated to accurately reflect the single-attempt behavior, or retry logic should be implemented if retries are intended.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini skip daily Gemini free-tier quota exhausted on this commit

The retry comment identified in the last review is corrected in 92b85ce. The current run failed with the daily requests quota error before reviewing that commit. Local browser, TypeScript, CSS reproduction, site assembly, and billing validation checks pass.

Thanks,
Gerard

@github-actions

Copy link
Copy Markdown

Gemini Review skipped on 92b85ce by @gerardrecinto. Reason: daily Gemini free-tier quota exhausted on this commit. A new push needs a fresh review or a new skip.

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 91d9a2b5c70b0582e5d31af4ba3a05df529ec393
Verdict: FAIL

Here are the actionable findings from the diff review:

  • Correctness bug: demo/index.html - The primary hero content (#hero-wrap) and the "scroll story" (.story) are positioned outside the main HTML element. The main element is intended to encapsulate the dominant content of the <body>. Placing significant, primary content outside of main is an ARIA semantic violation and can negatively impact accessibility tools, as users relying on them might miss this content. The main element now starts after the initial hero and scroll story, which are clearly central to the page's purpose.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

WebKit can leave animation.finished unresolved for animations that already finished, which timed out the /private-intent/ scan.

Thanks,
Gerard
@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

The earlier skip was for the Gemini quota. This commit also fixes the WebKit a11y timeout (animation.finished never resolving for finished animations), so a fresh review is welcome.

Thanks,
Gerard

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 52ac465cb4ce94e15b05aec5a36f8cb26d9c53e2
Verdict: PASS

The prior finding "Security: Server-Side Input Validation for Billing API" is unsupported by the provided diff and the explanation within the dispute. The dispute correctly states that server-side validation, rate limiting, and type-safe storage (B-tree without SQL/command execution) are already implemented in the Go backend. The client-side changes in this diff (introduction of postJson, enhanced UI feedback, and conditional form resetting) interact with, but do not weaken or bypass, these existing server-side controls. escapeHtml is appropriately used for displaying user input. Therefore, this finding is withdrawn.

The current diff introduces the following actionable findings:

  • Correctness: demo/index.html - Unconfirmed form submissions preserve user input. The handleFormSubmit function now correctly preserves user input in the form fields (form.reset() is conditional on serverSuccess). This is a positive change for user experience and resilience but was an unhandled edge case previously.
  • Correctness: demo/index.html - More specific error feedback for billing API interactions. The client-side handleFormSubmit and handleProCheckout functions now provide more granular feedback to the user, distinguishing between unconfirmed submissions (network errors), server errors, and successful but incomplete backend responses (e.g., no checkout URL). This improves error handling for various edge cases.
  • Performance: demo/index.html - WebAssembly engine and YouTube player are now lazy-loaded. The sop.wasm engine is no longer preloaded and is only initialized on user interaction or when a relevant section scrolls into view. The YouTube iframe for the demo video is also loaded only on user click. These are significant performance and privacy improvements.
  • Test Coverage: tests/homepage.spec.ts - New tests cover lazy loading and form submission outcomes. Comprehensive new tests have been added to verify the correct behavior of the WebAssembly engine's lazy loading, the YouTube player's lazy loading, and the various success/failure paths for form submissions. This strengthens the test suite for the new functionality.

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 52ac465cb4ce94e15b05aec5a36f8cb26d9c53e2
Verdict: PASS

No actionable findings.

Keeps the hero first and carries the platform install links from #700 into it.

Thanks,
Gerard
@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

Merged master. The hero conflict with #700 is resolved by keeping the hero first and carrying the platform install links into it, using this PR's text sizes. Hero, homepage and accessibility tests pass on Chromium, WebKit, Firefox and mobile Safari.

Thanks,
Gerard

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 67139dc0d3d2c5b3dd98ad63a89680ed3594c715
Verdict: FAIL

  • Correctness: In demo/index.html, the openProCheckoutModal function adds backendReached: result.status !== 0 to localStorage when storing Pro orders. result.status !== 0 indicates whether the billing service responded at all, but does not strictly mean the backend was reached successfully. For instance, a 4xx error would result in result.status !== 0 but is not a successful backend interaction in terms of generating a checkout. The name backendReached could be misleading; backendResponded might be more accurate or the logic adjusted if it's meant to indicate a successful backend interaction. However, the subsequent status: result.status field provides the full context, so this is a minor naming clarity point rather than a bug.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

The server answered, which includes a 4xx. Call it backendResponded.

Thanks,
Gerard
@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

Renamed the saved flag to backendResponded, since a 4xx is an answer but not a successful checkout. Nothing reads the old key.

Thanks,
Gerard

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 2393a342a8e6cd4dca9adb3289d8859b86ea233a
Verdict: PASS

  • Correctness Bug: Unconfirmed form submissions should not reset.
    demo/index.html (L2790): The enterprise inquiry form now correctly retains user input if the submission to the server is unconfirmed (e.g., network error, server error). This is a good improvement, preventing data loss for the user.
  • Performance Improvement: Deferred WebAssembly Engine Loading.
    demo/index.html (L68, L2991): The WebAssembly engine (sop.wasm) is no longer preloaded and its initialization is deferred until the first user interaction or when a live section requiring it scrolls into view. This significantly improves initial page load performance, especially for mobile users, by avoiding a 6MB download upfront.
  • Performance Improvement: Deferred YouTube Video Loading.
    demo/index.html (L725): The embedded YouTube player is now replaced with a click-to-play thumbnail. The YouTube player scripts (approximately 5MB) are only loaded when the user explicitly clicks to play the video. This improves initial page load performance.
  • Test Improvement: Enhanced Billing API Interaction Tests.
    tests/homepage.spec.ts: New tests (an inquiry is posted once and reports ${outcome} accurately) have been added to thoroughly verify the behavior of the postJson helper function and the UI feedback for various billing API submission outcomes (accepted, server error, lost response). This greatly improves test coverage and confidence in the robustness of the billing forms.
  • Test Improvement: WebAssembly Engine Lifecycle Testing.
    tests/homepage.spec.ts (the 6 MB engine downloads only after the visitor interacts): A new test explicitly verifies that the WebAssembly engine is not downloaded until user interaction, validating the performance optimization.
  • Test Improvement: Robust Form Readiness Waiting.
    tests/helpers/wasm-lifecycle.ts (new function waitForFormReady): A new helper function has been introduced to ensure tests wait for forms to be ready, specifically accounting for a client-side velocity guard that prevents submissions within a short timeframe after page load. This makes form-related tests more reliable.

@gerardrecinto
gerardrecinto merged commit 43c50fa into master Oct 31, 2026
36 checks passed
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.

1 participant