Skip to content

feat(demo): a Cloudflare snapshot, so people who are not in the room can open it - #880

Merged
4444J99 merged 2 commits into
mainfrom
feat/styx-cloudflare-snapshot
Aug 13, 2026
Merged

feat(demo): a Cloudflare snapshot, so people who are not in the room can open it#880
4444J99 merged 2 commits into
mainfrom
feat/styx-cloudflare-snapshot

Conversation

@4444J99

@4444J99 4444J99 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Why

The LAN demo covers the people beside you. This covers everyone else — an investor, a remote tester, anyone who just needs to see and understand it.

The Cloudflare question, answered honestly

Cloudflare was the right instinct for the wrong half of the stack. The NestJS API cannot run on Workers — sharp is a native binary, bullmq/ioredis need long-lived connections, and Cloudflare has no managed Postgres.

But the presentation half ships fine: 47 of 54 built routes were already prerendered, the seven dynamic ones are parameterised over a small known set of synthetic ids, and the guided tour is entirely client-side. So this is a full static export with no API, no database and no Redis behind it.

How the data stays honest

Fixtures are captured by driving the real demo per persona and recording what the app actually requests. Hand-written fixtures drift silently and render a plausible, wrong screen — the worst failure a demo has.

  • Writes are refused in plain language, never faked into looking successful.
  • A path with no fixture says so, rather than returning empty data that reads as a real but empty product.
  • The capture refuses to write an empty snapshot — that guard caught two failed runs.

Static export mechanics

generateStaticParams was required for the two dynamic segments. Both pages are client components, which cannot carry that export, so it lives on segment layouts — and the realm chrome moved to RealmChrome.tsx so its layout can be a server component. Outside a snapshot build the helpers return [], leaving those routes rendering on demand exactly as before.

Three defects this surfaced, each fixed at its base

  1. trailingSlash broke the guided tour on the hosted build. usePathname() reports /tour/ while every registry key is /tour, so the panel silently vanished. Normalised, with a test.
  2. The capture recorded nothing, silently. It read response bodies without awaiting them before the next navigation; Playwright discards bodies on navigate.
  3. A machine-global gitignore rule for public/ would have dropped every fixture and shipped a snapshot with no data. Overridden in the repo, where the intent belongs.

Verification

Against the built export, with the local stack irrelevant:

  • Real per-route HTML, not an SPA fallback — serve -s masked exactly that, and made a text assertion pass against the tour panel's own words while every route served the landing page.
  • /dashboard/ renders its synthetic contracts, streak, truth log and test-credit balance.
  • Guided tour panel present on every route.
  • Zero requests to ports 4310/4311 — the snapshot never touches the live stack.
  • npx tsc --noEmit clean; jest lib/guided-tour 7/7.

Commands

npm run snapshot:capture   # fixtures from the running demo
npm run snapshot:build     # static export
npm run snapshot:serve     # preview on :4315
npm run snapshot:deploy    # wrangler pages deploy

Deploy is not run by this PR — publishing a public URL is the owner's call.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XmP1SLhXcZBmA7g4NM6PTY

Summary by Sourcery

Introduce a Cloudflare-friendly static snapshot mode of the demo that serves captured fixtures without any backend, including static exports for key dynamic routes and guided tour support.

New Features:

  • Add snapshot mode that serves API responses from per-persona JSON fixtures instead of a live backend.
  • Provide capture, build, serve, and deploy commands and scripts to generate and ship the Cloudflare demo snapshot.
  • Add persona-based snapshot fixtures and client-side persona switching for the hosted demo.

Bug Fixes:

  • Normalize guided tour route matching to handle trailing slashes so panels render correctly on the hosted build.
  • Ensure snapshot fixture capture waits for response bodies before navigation to avoid silently recording nothing.
  • Guard against empty persona fixture sets so a snapshot with no data cannot be shipped.

Enhancements:

  • Refactor realms layout into a server layout plus client RealmChrome to support generateStaticParams while preserving existing UI.
  • Add snapshot-specific static params for contracts and realms so dynamic routes can be fully exported in static builds.
  • Update API client to short-circuit to snapshot fixtures and enforce read-only behavior in snapshot mode.

Build:

  • Add a Cloudflare snapshot build mode that uses Next.js static export with unoptimized images and trailingSlash, and disables API rewrites.

Tests:

  • Extend guided tour registry tests to cover trailing-slash path matching.

Chores:

  • Add pre-generated demo snapshot fixture files to the repo for each synthetic persona.

…can open it

The LAN demo covers the people beside you. This covers everyone else: a fully static
export of the same app, served from Cloudflare Pages with no API, no PostgreSQL and no
Redis behind it. The guided tour is entirely client-side, so it comes along unchanged.

Cloudflare was the right instinct for the wrong half of the stack. The NestJS API
cannot run on Workers -- sharp is a native binary, bullmq/ioredis need long-lived
connections, and there is no managed Postgres -- but 47 of 54 built routes were already
prerendered, and the seven dynamic ones are parameterised over a small, known set of
synthetic ids. So the presentation half ships to the estate that already exists.

Reads are answered from fixtures captured by DRIVING the real demo per persona and
recording what the app actually requests. Hand-written fixtures drift silently and
render a plausible, wrong screen, which is the worst failure a demo has. Writes are
refused in plain language rather than faked, and a path with no fixture says so instead
of returning empty data that looks like a real but empty product.

Static export required generateStaticParams on the two dynamic segments. Both pages are
client components, which cannot carry that export, so it lives on segment layouts --
and the realm chrome moved to RealmChrome.tsx to let its layout be a server component.
Outside a snapshot build the helpers return [], leaving those routes rendering on demand
exactly as before.

Three defects this surfaced, each fixed at its base:

- trailingSlash made usePathname report "/tour/" while every registry key is "/tour",
  so the guided tour vanished on the hosted build. Normalised, with a test.
- The capture read response bodies without awaiting them before the next navigation;
  Playwright discards bodies on navigate, so it silently recorded nothing at all.
- A machine-global gitignore rule for `public/` would have dropped every fixture,
  shipping a snapshot with no data. Overridden in the repo, where the intent belongs.

Verified against the built export with the local stack irrelevant: real per-route HTML
rather than an SPA fallback (`serve -s` masked exactly that, and made a text assertion
pass against the tour panel's own words), the dashboard rendering its synthetic
contracts, streak and truth log, the tour panel present on every route, and zero
requests to ports 4310/4311.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmP1SLhXcZBmA7g4NM6PTY
@4444J99
4444J99 requested a review from jtenen as a code owner August 13, 2026 09:49
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@4444J99, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 69 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f206c529-0bef-46c5-aa8d-7b55a01d1050

📥 Commits

Reviewing files that changed from the base of the PR and between bfcb455 and d2135b0.

📒 Files selected for processing (19)
  • .gitignore
  • docs/demo/jessica-demo-runbook.md
  • package.json
  • scripts/demo/capture-snapshot.mjs
  • scripts/demo/snapshot.sh
  • src/web/app/contracts/[id]/layout.tsx
  • src/web/app/realms/[slug]/RealmChrome.tsx
  • src/web/app/realms/[slug]/layout.tsx
  • src/web/lib/guided-tour/registry.test.ts
  • src/web/lib/guided-tour/registry.ts
  • src/web/lib/snapshot-params.ts
  • src/web/next.config.js
  • src/web/public/demo-snapshot/alecto.json
  • src/web/public/demo-snapshot/hr.json
  • src/web/public/demo-snapshot/moira.json
  • src/web/public/demo-snapshot/river.json
  • src/web/public/demo-snapshot/sage.json
  • src/web/services/api-client.ts
  • src/web/services/snapshot.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a Cloudflare-ready static snapshot of the demo by introducing a snapshot mode that serves captured API fixtures, wiring Next.js static export for specific dynamic routes, fixing guided tour trailing-slash routing, and adding scripts plus guards to capture, build, serve, and deploy the snapshot reliably without altering normal demo behavior.

Sequence diagram for API requests in Cloudflare snapshot mode

sequenceDiagram
  actor User
  participant BrowserApp
  participant api_client_request as request
  participant snapshot as snapshotRespond
  participant BackendAPI

  User->>BrowserApp: navigate /dashboard
  BrowserApp->>api_client_request: request("/dashboard/summary")
  api_client_request->>api_client_request: isSnapshotMode()
  alt snapshot mode
    api_client_request->>snapshot: snapshotRespond("/dashboard/summary", "GET")
    alt fixture found
      snapshot-->>api_client_request: { ok: true, data }
      api_client_request-->>BrowserApp: data
      BrowserApp-->>User: render snapshot data
    else missing fixture
      snapshot-->>api_client_request: { ok: false, status: 404, message }
      api_client_request-->>BrowserApp: ApiError(message, 404)
      BrowserApp-->>User: show read-only / missing screen
    end
  else normal demo
    api_client_request->>BackendAPI: HTTP GET /api/dashboard/summary
    BackendAPI-->>api_client_request: 200 JSON
    api_client_request-->>BrowserApp: data
    BrowserApp-->>User: render live data
  end
Loading

File-Level Changes

Change Details Files
Introduce a snapshot mode that serves API responses from per-persona fixtures and enforces read-only behavior.
  • Add snapshot service utilities to detect snapshot mode, load persona fixtures, manage persona selection, and respond to API paths with either captured data or clear error messages for writes/missing fixtures.
  • Short-circuit the shared API client to route all requests through the snapshot responder when snapshot mode is enabled, throwing ApiError on non-OK snapshot responses.
  • Define a mapping between synthetic demo emails and fixture file names so persona selection aligns with captured data.
src/web/services/snapshot.ts
src/web/services/api-client.ts
Configure Next.js to build a fully static Cloudflare snapshot and declare static params for dynamic segments without affecting normal builds.
  • Add a snapshot flag driven by NEXT_PUBLIC_STYX_SNAPSHOT and conditionally set Next.js output to export, disable image optimization, and enable trailingSlash for snapshot builds.
  • Prevent rewrites in snapshot mode so the client never calls /api behind a static export.
  • Introduce snapshot-params helpers and static generateStaticParams implementations for realms and contracts dynamic routes that return real seeded IDs/slugs only in snapshot mode, preserving on-demand rendering otherwise.
  • Split the realms layout into a server layout that hosts generateStaticParams and a client RealmChrome component holding existing UI chrome logic.
src/web/next.config.js
src/web/lib/snapshot-params.ts
src/web/app/realms/[slug]/layout.tsx
src/web/app/realms/[slug]/RealmChrome.tsx
src/web/app/contracts/[id]/layout.tsx
Add tooling to capture per-persona API fixtures from a live demo and to build, serve, and deploy the snapshot safely.
  • Create a Node/Playwright capture script that logs in as each synthetic persona, navigates guided-tour-derived routes, records successful GET /api responses into persona-specific JSON files, and refuses to pass if any persona yields zero fixtures.
  • Ensure response bodies are fully awaited before navigation to avoid silent fixture loss and add diagnostics for unreadable bodies.
  • Add a Bash snapshot.sh driver with subcommands to capture fixtures from the native demo state, build a snapshot export with appropriate env vars, serve the built snapshot locally as static HTML per route, and deploy to Cloudflare Pages, with guards against missing/empty fixtures and clear notes about .next being overwritten.
  • Expose npm scripts that wrap the snapshot.sh subcommands for capture, build, serve, and deploy operations.
scripts/demo/capture-snapshot.mjs
scripts/demo/snapshot.sh
package.json
Fix guided tour routing to work correctly with trailingSlash in the hosted snapshot and add coverage for it.
  • Normalize pathnames by stripping trailing slashes before matching against the guided tour registry, so routes like /tour/ and /dashboard/ correctly resolve to their registry entries.
  • Update guided tour route matching tests to cover trailing-slash inputs for key routes and ensure they map to the expected registry paths.
src/web/lib/guided-tour/registry.ts
src/web/lib/guided-tour/registry.test.ts
Add captured demo snapshot fixture files for each synthetic persona.
  • Include JSON fixture files under the public demo-snapshot directory for each persona used by snapshot mode (river, moira, hr, alecto, sage), enabling static serving of their captured API responses in the snapshot export.
src/web/public/demo-snapshot/alecto.json
src/web/public/demo-snapshot/hr.json
src/web/public/demo-snapshot/moira.json
src/web/public/demo-snapshot/river.json
src/web/public/demo-snapshot/sage.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

…traps

Records what the snapshot deliberately cannot do (writes are refused, not faked) and
the two traps that cost real time building it: a snapshot build overwrites .next so the
local demo afterwards answers from fixtures instead of calling /api, and previewing with
`serve -s` returns the landing page for every route with a 200 while the URL bar still
looks correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmP1SLhXcZBmA7g4NM6PTY

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • The snapshot capture script and snapshot.ts both hard-code the persona identifiers; consider centralising this mapping so adding or renaming a persona can’t diverge between capture and runtime.
  • readRegistryRoutes in capture-snapshot.mjs parses the guided tour registry source with regex, which is brittle to formatting/ordering changes; exposing a structured route/persona export from the registry would make the capture more robust.
  • setPersona currently accepts any string and persists it, even if there is no corresponding fixture file; you might want to constrain it to SNAPSHOT_PERSONAS to avoid confusing 404s when switching personas.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The snapshot capture script and snapshot.ts both hard-code the persona identifiers; consider centralising this mapping so adding or renaming a persona can’t diverge between capture and runtime.
- readRegistryRoutes in capture-snapshot.mjs parses the guided tour registry source with regex, which is brittle to formatting/ordering changes; exposing a structured route/persona export from the registry would make the capture more robust.
- setPersona currently accepts any string and persists it, even if there is no corresponding fixture file; you might want to constrain it to SNAPSHOT_PERSONAS to avoid confusing 404s when switching personas.

## Individual Comments

### Comment 1
<location path="src/web/services/snapshot.ts" line_range="89-90" />
<code_context>
+  const verb = method.toUpperCase();
+
+  // Sign-in is a persona switch, not an authentication.
+  if (verb === 'POST' && path === '/auth/login') {
+    // A fixed placeholder, not a credential: the snapshot has no auth to grant.
+    return { ok: true, data: { token: 'snapshot-session', userId: getPersona() } as T }; // allow-secret: placeholder session marker
+  }
</code_context>
<issue_to_address>
**issue (bug_risk):** Login handling doesn’t actually switch personas based on the supplied email.

In snapshot mode, `POST /auth/login` ignores the request body and always returns `{ token: 'snapshot-session', userId: getPersona() }`, so logging in with different synthetic emails never changes the active persona unless something else calls `setPersona`. Since `SNAPSHOT_PERSONAS` maps emails to personas, `snapshotRespond` should read the posted email, look up the persona, call `setPersona`, and return a matching `userId`. As-is, the snapshot appears to support multiple accounts but will always show River’s data.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +89 to +90
if (verb === 'POST' && path === '/auth/login') {
// A fixed placeholder, not a credential: the snapshot has no auth to grant.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Login handling doesn’t actually switch personas based on the supplied email.

In snapshot mode, POST /auth/login ignores the request body and always returns { token: 'snapshot-session', userId: getPersona() }, so logging in with different synthetic emails never changes the active persona unless something else calls setPersona. Since SNAPSHOT_PERSONAS maps emails to personas, snapshotRespond should read the posted email, look up the persona, call setPersona, and return a matching userId. As-is, the snapshot appears to support multiple accounts but will always show River’s data.

@4444J99
4444J99 merged commit 973a68a into main Aug 13, 2026
14 checks passed
@4444J99
4444J99 deleted the feat/styx-cloudflare-snapshot branch August 13, 2026 09:56

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21bc72d46d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +151 to +154
if (isSnapshotMode()) {
const snapshot = await snapshotRespond<T>(path, method);
if (snapshot.ok) return snapshot.data;
throw new ApiError(snapshot.message, snapshot.status, null, null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Route every snapshot request through the fixture responder

In the static Cloudflare export, this intercepts only calls made through api-client, while snapshot tour routes such as app/realms/page.tsx, app/realms/[slug]/page.tsx, and app/practitioner/page.tsx still call /api/... with fetch directly. Cloudflare has no API or rewrite layer, so those requests return errors instead of the captured fixture data; the realm detail consequently renders without its captured stats/contracts, and the practitioner chapter renders an API error. The snapshot mode needs to cover these direct callers as well (or migrate them to the fixture-aware client).

Useful? React with 👍 / 👎.

Comment on lines +89 to +91
if (verb === 'POST' && path === '/auth/login') {
// A fixed placeholder, not a credential: the snapshot has no auth to grant.
return { ok: true, data: { token: 'snapshot-session', userId: getPersona() } as T }; // allow-secret: placeholder session marker

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Set the requested persona during snapshot login

When someone signs into the snapshot as Moira, HR, Alecto, or Sage, this branch ignores the submitted email and returns getPersona() without ever calling setPersona; the request body is not even passed to snapshotRespond. Since local storage therefore remains unset, all subsequent reads use River's fixture, making the four other committed persona fixtures unreachable and causing their chapter-specific endpoints to return snapshot 404s.

Useful? React with 👍 / 👎.

Comment on lines +144 to +146
const file = path.join(outDir, `${persona}.json`);
await writeFile(file, `${JSON.stringify(fixtures, null, 2)}\n`, "utf8");
written.push({ persona, routes: routes.length, fixtures: Object.keys(fixtures).length });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate captures before overwriting existing fixtures

If a capture produces no API responses—the failure mode explicitly handled below—this write has already replaced the persona's previously valid fixture with {} before the script throws. snapshot.sh build checks only that JSON files exist, not that they contain entries, so a user who retries the build after the failed capture can publish an empty snapshot; collect and validate all persona results before replacing the committed files.

Useful? React with 👍 / 👎.

Comment on lines +22 to +25
'c1000000-0000-0000-0000-000000000001',
'c1000000-0000-0000-0000-000000000002',
'c1000000-0000-0000-0000-000000000003',
'c1000000-0000-0000-0000-000000000004',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Export River's second contract route

River's captured GET /contracts response contains c1000000-0000-0000-0000-00000000000a, and the dashboard maps every returned contract to /contracts/${id}, but this static-parameter list stops at ...0004. In the Cloudflare export, clicking River's second visible contract therefore reaches a route for which no HTML was generated; include ...000a and capture its detail fixture as well.

Useful? React with 👍 / 👎.

Comment on lines +153 to +154
const empty = written.filter((entry) => entry.fixtures === 0);
if (empty.length) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject incomplete persona captures

The success check accepts any persona with one captured response, even when the persona's primary tour route failed. The committed alecto.json, for example, contains only GET /users/me, while /fury immediately requests GET /fury/queue and GET /fury/stats; after persona switching is fixed, both calls receive snapshot 404s and the core auditor chapter shows an empty, disconnected workbench. Validate required endpoints or route failures per persona rather than checking only whether the total is zero.

Useful? React with 👍 / 👎.

Comment thread scripts/demo/snapshot.sh
Comment on lines +58 to +62
NEXT_PUBLIC_STYX_SNAPSHOT=true \
NEXT_PUBLIC_STYX_TEST_MONEY_MODE=true \
NEXT_PUBLIC_STYX_PRIVATE_BETA=true \
NEXT_PUBLIC_STYX_ENV_LABEL=cloudflare-snapshot \
NEXT_PUBLIC_STYX_FEATURE_B2B_HR_UI=true \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Configure hosted feedback separately from the LAN collector

This enables the guided tour in the hosted build through NEXT_PUBLIC_STYX_TEST_MONEY_MODE, but it leaves the tour's feedback client targeting its default same-host port 4312. On an HTTPS Cloudflare Pages URL that resolves to https://<pages-host>:4312, where the presenter's LAN-only collector cannot exist, so all route telemetry is discarded and every remote viewer who submits a visible note receives a failure. Supply a reachable hosted collector URL or disable the feedback controls for snapshot builds.

Useful? React with 👍 / 👎.

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