# Project export: Auralis

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Auralis turns an audiogram into a guided listening comparison, helping families hear how communication, distance and background sounds can affect everyday conversation.
- Devpost: https://devpost.com/software/auralis-jn26um
- GitHub: https://github.com/OnFiala/auralis-build-week-2026
- Demo: https://auralis-build-week-2026.vercel.app/
- Video: https://www.youtube.com/embed/eI4U4M1iEkc?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Auralis_Build_Week_2026 (39 commits)

## Devpost submission (written by the team)

### Inspiration

For a long time, I have been thinking about how to help the family of a person with hearing loss understand the real problems that person faces. This project grew from that idea. The inspiration comes from my work in audiology, where I meet people who live with hearing loss as well as their families.

### What it does

The layer built during Build Week guides the user through a situation as heard by a person with healthy hearing and compares it with the same situation from the perspective of a person with hearing loss. The application journey is designed specifically for family members, so they can better imagine the difficulty their loved one is dealing with. How I built it I developed the project in close cooperation with ChatGPT and the Codex app. I gradually locked down the architecture, which I had been building from the first day of my registration for Build Week, before the official submission period began. When Build Week officially started, I began creating and building the complete system, as can be seen through my session ID. Challenges I ran into In general, I often have a problem with projects growing beyond their original scope. You always think of another improvement, and the architecture gradually starts to break down as more layers are added. This time, I managed to keep the scope locked, but I ran into a relatively restrictive deadline that became quite tight for me. The frontend and the overall execution of the system could and should be more than one level better. Creating a video for my product was also a challenge, and the result should definitely be more than one level further developed. Accomplishments that I’m proud of I managed to build the entire project on time and get it genuinely working, document it, maintain the scope, and keep everything within clearly locked boundaries. I may have layered the architecture more than would have been ideal, but overall, I am proud of the product. What I learned I am essentially learning all the time. Development in the Codex app and ChatGPT app is moving very quickly, and I am very glad that projects like this help me stay up to date.

### What's next

The project should be much broader, with many more options, and it should also include scene generation through GPT Image 2.0. Ideally, GPT Realtime could be used, for example, to transform ordinary communication into an illustrative hearing-loss perspective. The entire second branch for a person with hearing loss is also still missing. It would help them explore what better correction might feel like, potentially using, for example, the hearing-health functionality of AirPods Pro. It would also be possible and useful to present options for correcting hearing loss, different hearing-aid brands, and many other possibilities. This text was translated with AI GPT-5.6 Sol, not AI generated, to achieve the most accurate possible Czech-to-English translation.

## README (from the GitHub repository)

# Auralis

> See what hearing sounds like.

**Auralis** is the product. **Auralis WebMCP** is the current shared human-agent edition.

## Judge quick start — five minutes

**Live app:** [auralis-webmcp.vercel.app](https://auralis-webmcp.vercel.app)

**Public source:** [github.com/OnFiala/auralis-webmcp](https://github.com/OnFiala/auralis-webmcp)

Auralis is a non-diagnostic listening experience for families. WebMCP turns the existing human journey into a shared live session: the agent uses page-owned semantic tools, the interface performs a full Agent Reveal, and the person retains consent, listening-level, playback, and Stop authority.

1. Open the live app in the ChatGPT desktop app's built-in browser and use **GPT-5.6 Sol or GPT-5.6 Terra** with Site Tools enabled.
2. In the browser address bar, open **Site tools → Available site tools** to inspect the seven Auralis tools.
3. Tell the agent: **“Start a guided Auralis family listening session. Use the page's Site Tools, explain each step in English, and continue waiting for my visible choices.”**
4. Confirm the illustrative consent, choose a synthetic demo audiogram, set a comfortable headphone level, and optionally allow agent-started playback.
5. Listen to A then B, describe the difference in everyday language, try one suggested change such as turning the television off, and review the deterministic summary.

| WebMCP tool | What it lets the agent do |
|---|---|
| `get_auralis_state` | Read the current phase, allowed next tools, grounded choices, and human gate. |
| `stage_demo_session` | Start Agent Reveal or select one approved synthetic demo profile after consent. |
| `prepare_comparison` | Prepare the baseline or one bounded single-variable adaptation. |
| `play_listening_comparison` | Start authorized A, B, or A-then-B playback while preserving human Stop. |
| `record_user_observation` | Translate only the person's stated experience into bounded non-diagnostic evidence. |
| `get_session_evidence` | Read the sanitized ordered session record and summary readiness. |
| `wait_for_session_change` | Wait on the current revision for human or technical progress without model polling. |

## What Auralis is

Auralis turns an audiogram into a guided listening comparison. A user chooses an illustrative hearing profile, enters the same 64-second family scene, compares the source reference with a deterministic profile result, and then tries changes such as reducing television sound or moving the important speaker closer.

Auralis is an educational, non-clinical experience. It does not diagnose hearing, reproduce an individual person's perception exactly, fit a hearing aid, prescribe treatment, or predict benefit.

The product is designed for a family member, partner, or friend who wants to understand why an everyday conversation may become difficult. Its core proof is deliberately simple: keep the people, words, timing, and source scene the same, then change only the illustrated listening conditions. The person can hear both perspectives and describe the difference in ordinary language.

- **Live Auralis WebMCP production:** [auralis-webmcp.vercel.app](https://auralis-webmcp.vercel.app), publicly accessible without Vercel authentication
- **Pre-WebMCP public baseline:** [`53fa83e188a0c5e076c5a0f651f2d937b167954d`](https://github.com/OnFiala/auralis-webmcp/commit/53fa83e188a0c5e076c5a0f651f2d937b167954d); the original runtime implementation was established at [`384ece76a29ab4f3c82e69e1bc1c585044d82c29`](https://github.com/OnFiala/auralis-webmcp/commit/384ece76a29ab4f3c82e69e1bc1c585044d82c29)
- **Released WebMCP source:** [`7ee10c9471a8242691d49018f465701a7e6991af`](https://github.com/OnFiala/auralis-webmcp/commit/7ee10c9471a8242691d49018f465701a7e6991af) on `main`; `213f572` remains the verified security/dependency base and `2a856f0` the completed feature checkpoint before release-only work
- **Production evidence:** Vercel deployment `dpl_3ZxHa8qmfb1bvCP4Zq2aKQKge8gg` was verified Ready and source-bound to exact `7ee10c9` on September 2, 2026; use the public production URL above rather than an access-protected Preview
- **OpenAI API key:** optional; the complete deterministic journey works without one

## Two development eras

Auralis has two deliberately separate development eras:

### Original Auralis — OpenAI Build Week 2026

The July 2026 project created the complete human-controlled foundation:

- a seven-step guided browser journey;
- predefined profiles and exact manual bilateral audiogram entry;
- one validated 64-second synthetic family scene built from four synchronized stems;
- deterministic same-source A/B rendering in the browser;
- one-sided and bilateral illustrative support, television on/off, and speaker-position comparisons;
- explicit volume, playback, Stop, safety, privacy, and evidence boundaries;
- one optional sanitized GPT-5.6 Terra explanation that never controls the audio;
- automated validation, public deployment, and the original governance and design history.

That original application is preserved in public history through `53fa83e`; the WebMCP work is additive above that exact baseline on `main`. Its immutable Build Week documents remain historical provenance. Their dates, category, `/feedback` field, and submission rules do not govern the later WebMCP Challenge.

### Auralis WebMCP — WebMCP Challenge

The later work turns the same trusted listening core into a shared human-agent experience. The current competition period began on August 25, 2026. Owner records place WebMCP work no later than August 28; public Git evidence begins with [`8af992e`](https://github.com/OnFiala/auralis-webmcp/commit/8af992e8d9454060d07979eae9c6c9a23fc64bb2) on August 29, directly above the preserved `53fa83e` baseline.

The new public feature history adds:

- seven semantic WebMCP tools instead of DOM automation;
- one shared, revisioned session state for page actions and agent actions;
- agent-only activation and a full visual Agent Reveal;
- a deliberately different agent-guided interface while retaining the complete human interface;
- click, typed-agent, and voice-agent control over the same live session;
- bounded active waiting for human decisions without model polling;
- two formal human gates for consent and comfortable physical volume, plus a separate optional grant for agent-started playback;
- deterministic agent playback with a quiet lead-in, immediate human Stop, and reference-A replay;
- everyday-language observations mapped into bounded non-diagnostic evidence;
- a guided listen-observe-adapt loop with television-off and left-ear support as the preferred first changes;
- one deterministic agent summary and versioned visual/media provenance;
- stale-revision, idempotency, request-replay, failure, reset, and rollback protection.

The WebMCP work is additive and traceable. It does not rewrite or pretend to recreate the original Auralis history.

The current competition authority is the [WebMCP Challenge Official Rules](https://webmcp.devpost.com/rules), rechecked September 2, 2026. The exact product, evidence, rollback, and release boundary for the new work lives in [`docs/WEBMCP_PLAN.md`](docs/WEBMCP_PLAN.md).

## The signature WebMCP experience

The central product idea is not merely that an agent can click the existing website. Auralis exposes the meaning of the experience—state, choices, human gates, playback authority, observations, and evidence—through WebMCP. The agent works through those semantic tools while the person remains in control of the live page.

### A human page that transforms only for an agent

The landing page is always human-first. Merely browsing or clicking never enters agent mode. Its `Agentic ready` badge only explains that the site can be used with an agent.

When a compatible agent discovers the Site Tools and starts the guided session, the page performs a prominent full-surface **Agent Reveal**. The light human journey becomes a dark, cinematic agent-gu

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 45 recognized source files, 832 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (52 of 52)

```
.env.example
.gitignore
AGENTS.md
dependency-cruiser.cjs
docs/adr/0001-client-centric-single-deployment-topology.md
docs/adr/0002-typescript-react-nextjs-execution-environments.md
docs/adr/0003-anonymous-ephemeral-browser-session.md
docs/adr/0004-trusted-openai-gateway-and-model-contract.md
docs/adr/0005-canonical-hearing-profile.md
docs/adr/0006-deterministic-same-source-multistem-rendering.md
docs/adr/0007-browser-audio-safety.md
docs/adr/0008-single-semantic-experience-state.md
docs/adr/0009-bounded-gpt-lifecycle-and-degraded-state.md
docs/adr/0010-optional-unique-gpt-image.md
docs/adr/0011-attributable-session-evidence.md
docs/adr/0012-pseudonymous-operational-telemetry.md
docs/adr/0013-four-plane-critical-chain-testing.md
docs/Auralis_preparation_final.md
docs/AURALIS_VS_OPENAI_BUILD_WEEK_2026_REQUIREMENTS.md
docs/DEPENDENCY_MODEL.md
docs/OPENAI_BUILD_WEEK_2026_REQUIREMENTS_RECOMMENDATIONS.md
docs/PRESENT_TIME_FILE_TREE.md
docs/PRODUCT_FREEZE_VALIDATION.md
docs/RISK_MAP.md
docs/SYSTEM_DESIGN.md
docs/VERTICAL_SLICE_DEFINITION.md
LICENSE
next-env.d.ts
package.json
playwright.config.ts
public/media/family-dinner/manifest.json
README.md
src/app/api/health/route.ts
src/app/api/model/route.ts
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/browser/audio.ts
src/browser/evidence.ts
src/browser/model-client.ts
src/contracts/runtime.ts
src/core/core.test.ts
src/core/experience.ts
src/core/profile.ts
src/core/safety.ts
src/core/transformation.ts
src/server/model.test.ts
src/server/model.ts
src/server/openai.ts
tests/journey.spec.ts
tests/smoke.spec.ts
tsconfig.json
```

### Dependencies

- package.json: @playwright/test@1.61.1, @types/node@22.20.1, @types/react@19.2.17, @types/react-dom@19.2.3, dependency-cruiser@18.1.0, next@16.2.10, openai@6.48.0, react@19.2.7, react-dom@19.2.7, typescript@6.0.3, vitest@4.1.10, zod@4.4.3

### Recent commits (newest first)

- docs: finalize submission README and MIT license
- fix: improve listening guidance and Terra explanation
- polish: humanize and compact Auralis experience
- fix: harden final submission accessibility
- Polish explanation and completion surfaces
- Add visual communication interventions
- Add final A/B listening surface
- Add family scene and safety setup
- Add exact profile audiogram presentation
- Add guided comparison experience shell
- docs: close Phase 11 reality review
- fix: align Auralis product copy with Phase 10 reality
- docs: close Phase 10 vertical slice
- fix: expose accessible Auralis scene transcript
- feat: complete attributable Auralis journey
- feat: add coherent Auralis completion state
- feat: add coherent Auralis speaker-position intervention
- feat: add grounded Auralis live explanation
- feat: add coherent Auralis TV-off intervention
- feat: add functional Auralis profile choices

## Key source files (fetched from GitHub, selected and truncated for size)

### AGENTS.md

```markdown
# Auralis Project Operating Contract

## 1. Purpose and scope

This file is the repository-wide operating contract for Codex and all delegated agents working on Auralis.

It governs authority, phase order, interviews, change control, Git workflow, validation, documentation, delegation, research, security, reporting, and completion gates. It does not introduce product requirements, architecture, technology choices, implementation scope, or submission claims.

The primary Codex task owns the main project decisions and most core functionality. Owner-facing communication defaults to Czech unless the owner explicitly switches language. This file is normative in English.

In this contract, “primary Codex task” means the single user-visible Codex thread attached to this repository that owns project decisions, integration, most core functionality, and the future `/feedback` submission.

No nested `AGENTS.md`, `AGENTS.override.md`, secondary governance document, or user-visible secondary main implementation task may be created without explicit human approval.

## 2. Project authority hierarchy

For competition rules, obligations, compliance, deadlines, judging, and submission requirements, verified Official Rules and official program terms are the highest external authority.

Within the repository, read the following documents in this order:

1. `docs/OPENAI_BUILD_WEEK_2026_REQUIREMENTS_RECOMMENDATIONS.md`
   - Repository authority for competition requirements, compliance, deadlines, and submission obligations.

Within `docs/OPENAI_BUILD_WEEK_2026_REQUIREMENTS_RECOMMENDATIONS.md`, only items classified as `MUST` or `CONDITIONAL MUST`, together with facts directly supported by current official sources, are binding requirements. `OFFICIAL SHOULD`, `PROJECT SHOULD`, and `INFO` items are advisory or contextual unless the human owner explicitly adopts them as project decisions.

2. `docs/Auralis_preparation_final.md`
   - Authority for the frozen product intent, target problem, intended user experience, and original product scope.
3. `docs/AURALIS_VS_OPENAI_BUILD_WEEK_2026_REQUIREMENTS.md`
   - Analytical authority for rules-driven comparison, gap analysis, and recommendations.

The third document must not silently create product requirements or override either higher document.

This `AGENTS.md` governs agent behavior and project process. It must not be used to override competition rules or silently redefine the product.

`README.md`, subagent output, external research, and agent inference may provide context or evidence, but none of them may silently override the authority hierarchy.

If a current primary source conflicts with a frozen repository source, stop, identify the exact conflict and impact, and request an explicit human decision.

## 3. Locked and immutable files

Treat these files as immutable baseline snapshots:

- `docs/Auralis_preparation_final.md`
- `docs/AURALIS_VS_OPENAI_BUILD_WEEK_2026_REQUIREMENTS.md`
- `docs/OPENAI_BUILD_WEEK_2026_REQUIREME
[truncated — 19520 more characters]
```

### docs/PRESENT_TIME_FILE_TREE.md

```markdown
# Auralis Present-Time File Tree

## 1. Title and status

**Status:** Approved Phase 7 present-time file tree; Phase 10 complete

**Proposal date:** July 15, 2026  
**Scope:** Phase 9 executable walking skeleton and Phase 10 approved Family slice only

## 2. Purpose

This document maps the approved system and dependency boundaries to the smallest concrete repository paths needed for Phase 9 and Phase 10.

Phase 9 establishes only an executable, deployable application shell. Phase 10 adds the approved Auralis critical chain. Existing governance and design documents remain unchanged and are omitted from the tree.

## 3. Governing constraints

- Use one strict TypeScript, React and Next.js App Router application with one browser runtime and one trusted server boundary.
- Keep Phase 9 free of Auralis domain, audio, model, evidence and validated-media implementation.
- Keep pure contracts and browser-owned domain logic outside React and `app/`.
- Keep raw audiogram values browser-only and `ExperienceState` as the sole semantic authority once created in Phase 10.
- Permit direct Web Audio access only in `src/browser/audio.ts`.
- Permit the OpenAI SDK and server secret access only in `src/server/openai.ts`.
- Keep deterministic audio independent from GPT and optional image generation.
- Use no persistence, authentication, database, second backend or speculative extension layer.
- Add a path only when Phase 9 or the approved Phase 10 slice needs it.

## 4. Present-time tree

`[P9]` means first created for the executable walking skeleton. `[P10]` means deferred until vertical-slice implementation.

```text
.
├── package.json [P9]
├── package-lock.json [P9]
├── tsconfig.json [P9]
├── next-env.d.ts [P9]
├── playwright.config.ts [P9]
├── dependency-cruiser.cjs [P9]
├── .env.example [P10]
├── public/
│   └── media/family-dinner/
│       ├── manifest.json [P10]
│       ├── focused-speech.wav [P10]
│       ├── overlapping-speech.wav [P10]
│       ├── television.wav [P10]
│       └── kitchen-room.wav [P10]
├── src/
│   ├── app/
│   │   ├── layout.tsx [P9]
│   │   ├── page.tsx [P9]
│   │   ├── globals.css [P9]
│   │   └── api/
│   │       ├── health/route.ts [P9]
│   │       ├── model/route.ts [P10]
│   │       └── image/route.ts [P10]
│   ├── contracts/
│   │   └── runtime.ts [P10]
│   ├── core/
│   │   ├── profile.ts [P10]
│   │   ├── experience.ts [P10]
│   │   ├── transformation.ts [P10]
│   │   ├── safety.ts [P10]
│   │   └── core.test.ts [P10]
│   ├── browser/
│   │   ├── audio.ts [P10]
│   │   ├── model-client.ts [P10]
│   │   └── evidence.ts [P10]
│   └── server/
│       ├── model.ts [P10]
│       ├── openai.ts [P10]
│       └── model.test.ts [P10]
└── tests/
    ├── smoke.spec.ts [P9]
    └── journey.spec.ts [P10]
```

## 5. Path responsibilities

| Path | Responsibility | Phase 6 boundary | First created |
|---|---|---|---|
| `package.json`, `package-lock.json` | `package.json` and `package-lock.json` declare and lock only the dependencies required by t
[truncated — 12323 more characters]
```

### package.json

```
{
  "name": "auralis-build-week-2026",
  "version": "0.0.0",
  "private": true,
  "description": "An illustrative, non-clinical deterministic Auralis hearing experience for OpenAI Build Week 2026.",
  "engines": {
    "node": "22.x"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "typecheck": "tsc --noEmit",
    "check:deps": "depcruise --config dependency-cruiser.cjs -- src tests",
    "test:core": "vitest run src/core/core.test.ts",
    "test:model": "vitest run src/server/model.test.ts",
    "test:smoke": "playwright test tests/smoke.spec.ts",
    "test:journey": "playwright test tests/journey.spec.ts",
    "check": "npm run typecheck && npm run check:deps && npm run test:core && npm run test:model && npm run build && npm run test:smoke && npm run test:journey"
  },
  "dependencies": {
    "next": "16.2.10",
    "openai": "6.48.0",
    "react": "19.2.7",
    "react-dom": "19.2.7",
    "zod": "4.4.3"
  },
  "devDependencies": {
    "@playwright/test": "1.61.1",
    "@types/node": "22.20.1",
    "@types/react": "19.2.17",
    "@types/react-dom": "19.2.3",
    "dependency-cruiser": "18.1.0",
    "typescript": "6.0.3",
    "vitest": "4.1.10"
  },
  "overrides": {
    "postcss": "8.5.10"
  }
}

```

### src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import type { ReactNode } from "react";

import "./globals.css";

export const metadata: Metadata = {
  title: "Auralis — See what hearing sounds like",
  description:
    "A guided listening comparison that helps families explore how hearing profiles, distance, and background sound can shape an everyday conversation.",
};

export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

```

### src/app/api/health/route.ts

```typescript
export const runtime = "nodejs";
export const dynamic = "force-dynamic";

export function GET() {
  return Response.json(
    {
      status: "ok",
      service: "auralis-shell",
      phase: "9-walking-skeleton",
    },
    {
      status: 200,
      headers: {
        "Cache-Control": "no-store, max-age=0",
      },
    },
  );
}

```

### src/app/api/model/route.ts

```typescript
import {
  MAX_MODEL_REQUEST_BYTES,
  ModelRequestValidationError,
  parseModelExplanationRequestText,
} from "../../../contracts/runtime";
import { explainCurrentExperience } from "../../../server/model";
import { OpenAIExplanationProvider } from "../../../server/openai";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const responseHeaders = {
  "Cache-Control": "no-store",
  "Content-Type": "application/json; charset=utf-8",
} as const;

function invalidRequestResponse(): Response {
  return Response.json(
    { error: "invalid-request" },
    { status: 400, headers: responseHeaders },
  );
}

export async function POST(request: Request): Promise<Response> {
  const contentLength = request.headers.get("content-length");

  if (
    contentLength !== null &&
    Number.isFinite(Number(contentLength)) &&
    Number(contentLength) > MAX_MODEL_REQUEST_BYTES
  ) {
    return invalidRequestResponse();
  }

  try {
    const body = await request.text();
    const modelRequest = parseModelExplanationRequestText(body);
    const result = await explainCurrentExperience(
      modelRequest,
      new OpenAIExplanationProvider(),
    );

    return Response.json(result, {
      status: 200,
      headers: responseHeaders,
    });
  } catch (error) {
    if (error instanceof ModelRequestValidationError) {
      return invalidRequestResponse();
    }

    return Response.json(
      { error: "model-boundary-failure" },
      { status: 500, headers: responseHeaders },
    );
  }
}

```

### next-env.d.ts

```typescript
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

```

### playwright.config.ts

```typescript
import { defineConfig, devices } from "@playwright/test";

const publicBaseURL = process.env.PLAYWRIGHT_BASE_URL;
const localBaseURL = "http://127.0.0.1:3000";

export default defineConfig({
  testDir: "./tests",
  fullyParallel: false,
  retries: 0,
  workers: 1,
  reporter: "line",
  use: {
    baseURL: publicBaseURL ?? localBaseURL,
    trace: "retain-on-failure",
  },
  webServer: publicBaseURL
    ? undefined
    : {
        command: "npm run start -- --hostname 127.0.0.1 --port 3000",
        url: `${localBaseURL}/api/health`,
        reuseExistingServer: false,
        timeout: 120_000,
      },
  projects: [
    {
      name: "chromium",
      use: { ...devices["Desktop Chrome"] },
    },
  ],
});

```

### tests/smoke.spec.ts

```typescript
import { expect, test } from "@playwright/test";

test("public Auralis welcome reaches its trusted health boundary", async ({ page }) => {
  const configuredUrl = process.env.PLAYWRIGHT_BASE_URL;
  let authenticatedOrigin: string | undefined;

  if (configuredUrl) {
    const parsed = new URL(configuredUrl);

    if (parsed.searchParams.has("_vercel_share")) {
      await page.goto(parsed.toString(), { waitUntil: "domcontentloaded" });
      authenticatedOrigin = new URL(page.url()).origin;
    }
  }

  const pageUrl = authenticatedOrigin
    ? new URL("/", authenticatedOrigin).toString()
    : "/";
  const navigation = await page.goto(pageUrl);

  expect(navigation?.status()).toBe(200);
  expect(navigation?.headers()["www-authenticate"]).toBeUndefined();
  await expect(page).toHaveTitle("Auralis — See what hearing sounds like");
  const favicon = page.locator('link[rel~="icon"]').first();
  await expect(favicon).toHaveAttribute("href", /\/icon\.png/);
  await expect(page.getByRole("heading", { name: "Auralis", level: 1 })).toBeVisible();
  await expect(
    page.getByText(
      "Auralis turns an audiogram into a guided listening comparison.",
      {
        exact: true,
      },
    ),
  ).toBeVisible();
  await expect(
    page.getByText(
      "Choose a hearing profile, step into an everyday family scene, and hear how distance, background sound, and communication support can change what reaches a listener.",
      {
        exact: true,
      },
    ),
  ).toBeVisible();
  await expect(
    page.getByText(
      "Built to help families understand an everyday listening experience—not to diagnose hearing or recommend treatment.",
      {
      exact: true,
      },
    ),
  ).toBeVisible();
  await expect(
    page.getByText(
      "Best experienced with headphones — AirPods Pro, AirPods Max, or any comfortable stereo headphones. Set a comfortable listening level before you begin.",
      { exact: true },
    ),
  ).toBeVisible();
  await expect(
    page.getByRole("button", { name: "Check system status" }),
  ).toHaveCount(0);
  await page.getByRole("button", { name: "Start the comparison" }).click();
  await expect(
    page.getByRole("heading", { name: "Choose a listening profile" }),
  ).toBeFocused();

  const healthUrl = authenticatedOrigin
    ? new URL("/api/health", authenticatedOrigin).toString()
    : "/api/health";
  const health = await page.context().request.get(healthUrl);
  expect(health.status()).toBe(200);
  expect(health.headers()["www-authenticate"]).toBeUndefined();
  expect(health.headers()["cache-control"]).toContain("no-store");
  await expect(health.json()).resolves.toEqual({
    status: "ok",
    service: "auralis-shell",
    phase: "9-walking-skeleton",
  });
});

```

### src/browser/model-client.ts

```typescript
import {
  MODEL_ID,
  MODEL_OPERATION,
  modelExplanationRequestSchema,
  modelExplanationResponseSchema,
  type DegradedModelExplanation,
  type ModelExplanationRequest,
  type ModelExplanationResponse,
} from "../contracts/runtime";

const CLIENT_TIMEOUT_MS = 18_000;
const DEGRADED_MESSAGE =
  "Live GPT explanation is unavailable. The deterministic audio comparison remains available." as const;

function clientDegraded(
  request: ModelExplanationRequest,
  reason: "client-timeout" | "network-failure" | "invalid-response",
): DegradedModelExplanation {
  return Object.freeze({
    status: "degraded" as const,
    operation: MODEL_OPERATION,
    runId: request.runId,
    attemptId: request.attemptId,
    attemptNumber: request.attemptNumber,
    groundingRevision: request.groundingRevision,
    sourceIdentity: request.sourceIdentity,
    resultIdentity: request.resultIdentity,
    model: MODEL_ID,
    responseId: null,
    reason,
    message: DEGRADED_MESSAGE,
  });
}

export async function requestLiveExplanation(
  input: ModelExplanationRequest,
): Promise<ModelExplanationResponse> {
  const parsedRequest = modelExplanationRequestSchema.safeParse(input);

  if (!parsedRequest.success) {
    throw new Error("The browser model client received an invalid request.");
  }

  const request = parsedRequest.data;
  const controller = new AbortController();
  const timeout = globalThis.setTimeout(
    () => controller.abort(),
    CLIENT_TIMEOUT_MS,
  );

  try {
    const response = await fetch("/api/model", {
      method: "POST",
      cache: "no-store",
      credentials: "same-origin",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify(request),
      signal: controller.signal,
    });

    if (!response.ok) {
      return clientDegraded(request, "invalid-response");
    }

    const parsedResponse = modelExplanationResponseSchema.safeParse(
      await response.json(),
    );

    return parsedResponse.success
      ? parsedResponse.data
      : clientDegraded(request, "invalid-response");
  } catch (error) {
    return clientDegraded(
      request,
      error instanceof DOMException && error.name === "AbortError"
        ? "client-timeout"
        : "network-failure",
    );
  } finally {
    globalThis.clearTimeout(timeout);
  }
}

```

[29 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]