# Project export: Scout - New Age Interview Pattern

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: Scout is made to replace traditional style Leetcode Interviews, and replace them with real software engineering challenges, which reflects their day to day job scenarios
- Devpost: https://devpost.com/software/scout-interview-pattern
- GitHub: https://github.com/Navigam1108/BuildWeekProject
- Video: https://www.youtube.com/embed/Tb3L3NIe17w?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Navigam (12 commits)

## Devpost submission (written by the team)

### Overview

Project name Scout - Repository Interviews Elevator pitch Scout replaces LeetCode-style coding rounds with browser-based repository interviews, benchmarks, and evidence-based scoring. Built with Next.js, React, TypeScript, Node.js, SQLite, Docker, code-server, Python, C++, OpenAI Codex, GPT-5.6, Vercel AI SDK Try it out Source code: https://github.com/Navigam1108/BuildWeekProject Demo video: https://youtu.be/Tb3L3NIe17w About the project

### Inspiration

Most engineering interviews still test isolated puzzles in a blank editor. That makes it easy to optimize for memorized patterns or a single AI-generated answer, while missing the work engineers actually do: understand an unfamiliar system, find the important bottleneck, preserve behavior, test the change, and explain the trade-off. Scout turns that work into the interview.

### What it does

Scout gives candidates a realistic, browser-based VS Code workspace backed by an isolated Docker container. Each session starts from a production-shaped repository, engineering ticket, tests, benchmark, and intentional TODO seams. The prototype includes nine Python, TypeScript, and C++ interview repositories with 45 measurable optimization opportunities across routing, caching, scheduling, matching, payments, recommendations, search, log processing, and risk allocation. Candidates are not expected to finish every mission. The scorecard rewards meaningful progress: correctness, benchmark improvement, progress toward a golden reference, and hidden-test confidence. Two or three strong improvements can be a strong interview result. Interviewers can view elapsed time, the live candidate VS Code workspace, submissions, test evidence, benchmark deltas, mission scores, and the data-structure concepts demonstrated.

### How we built it

Scout uses Next.js, React, TypeScript, SQLite, Docker, and code-server. Starting a session copies a candidate repository, applies a reproducible workload variant, launches the matching language container, and opens VS Code in the browser. The terminal and hidden grader use the same workspace, so candidate tests, benchmarks, and the submitted code run in one toolchain. The grader runs hidden invariants and benchmarks, parses a structured report, and generates the interviewer scorecard. Each pack has a candidate version, golden reference, visible tests, hidden tests, and calibrated mission rubric. The AI interview guide is honestly shown as Coming soon. This demo focuses on the working repository workflow and deterministic evidence rather than presenting an unfinished assistant as a completed feature. How we used OpenAI Codex and GPT-5.6 I used OpenAI Codex with GPT-5.6 as a development collaborator. It helped decompose the work, iterate on the Next.js and Docker workflow, structure challenge packs, improve the UI, troubleshoot grading and build issues, and refine documentation and tests. I set the interview philosophy, designed the repository missions and scoring rubric, reviewed the implementation, ran the validation checks, and made the final product decisions. GPT-5.6 is not Scout's runtime interviewer or grader: current scores are deterministic and come from tests and benchmarks. Challenges and learnings The main challenge was balancing realism with reproducible evaluation. Scout uses persisted replay variants to change workload shape and ticket framing while keeping the public API and rubric comparable. We also avoided an all-or-nothing grade because real candidates will not optimize every path in one interview. We learned that the best interview evidence is not a pass/fail label. It is a clear record of what the candidate changed, what they tested, how performance moved, and what opportunities remained.

### What's next

Next steps are authentication, protected grader storage, read-only live observation, more calibrated repositories, and a constrained AI guide for repository navigation rather than solution generation.

## README (from the GitHub repository)

# Scout - repository interviews for the AI era

> An OpenAI Builder Week project that replaces puzzle-style coding rounds with realistic, browser-based codebase investigations.

Scout gives a candidate a production-shaped repository instead of an isolated
algorithm prompt. They identify and improve the bottlenecks they can reach;
the interviewer receives reproducible tests, benchmark evidence, and
mission-level progress. The AI interview guide is intentionally presented as a
coming-soon preview while the repository experience takes center stage.

The goal is not to make interviews "AI-proof." It is to make engineering
judgment visible: how someone maps an unfamiliar system, chooses a bottleneck,
checks invariants, improves performance, and explains the trade-offs.

## What judges can try

| Capability | What it provides |
| --- | --- |
| **9 repository environments** | Python, TypeScript, and C++ services spanning logs, payments, caching, matching, scheduling, recommendations, search, routing, and risk allocation. |
| **45 measurable missions** | Every pack has five independent performance opportunities, distributed across a multi-file codebase rather than exposed as named DSA exercises. |
| **Partial-success grading** | Completing two or three missions well is a strong result. The scorecard shows correctness, benchmark progress, and proximity to a golden reference. |
| **Browser-native workspace** | Each session creates a fresh repository inside a Docker-backed code-server IDE, with an integrated terminal for `make test` and `make bench`. |
| **Replay variants** | A persisted per-session seed changes workload shape and ticket framing while keeping the public API and mission rubric comparable. |
| **AI interview guide (coming soon)** | The demo previews a planned, constrained codebase guide instead of exposing an empty chatbox. The repository, scoring, and browser workflow are available today. |
| **Interviewer evidence** | The live view shows elapsed time, submissions, visible and hidden test totals, benchmark deltas, mission cards, concepts, and the session log. |
| **Open-ended sessions** | Tasks are designed for roughly an hour, but Scout never hard-stops a session; the interviewer sees elapsed time and ends it manually. |

## Fastest judge path

The shortest end-to-end demo starts the Python LogScope interview. It requires
Node.js **22.6+**, Git, ripgrep (`rg`), and a Docker daemon running Linux
containers. Use Docker Desktop on Windows or macOS, or Docker Engine on Linux.
On Linux, make sure your current user can run `docker` without `sudo`. Python
is included in the candidate image, so it is not needed on the host for this
flow.

### Windows PowerShell

```powershell
git clone <repository-url>
Set-Location openai_interview_platform
Copy-Item .env.example .env
npm ci

# Builds the image needed for the LogScope Python task.
docker build -t challenge-py images/challenge-py

npm run dev
```

### Linux / macOS Bash

```bash
git clone <repository-url>
cd openai_interview_platform
cp .env.example .env
npm ci

# Builds the image needed for the LogScope Python task.
docker build -t challenge-py images/challenge-py

npm run dev
```

Open [http://localhost:3000/admin](http://localhost:3000/admin), click **Start
session** on **LogScope**, then open the candidate workspace link shown by the
app. Enter the generated IDE password when code-server asks for it. In the
browser terminal, run:

```bash
make test
make bench
```

Click **Submit**, then open the interviewer view to inspect the grade report.
Use **End session** when finished to remove the container.

The first image build downloads the pinned code-server bundle and can take a
little longer. Docker Hub and GitHub access are needed during that build.

### Run the entire portfolio

Build all three language images before starting TypeScript or C++ tasks:

```powershell
npm run images:build
```

This is equivalent to building `challenge-py`, `challenge-ts`, and
`challenge-cpp` individually. The images run code-server as a non-root
candidate user and include only the language runtime, compiler/tooling, Git,
Make, and support needed for the browser workspace and grader.

`PUBLIC_HOST` in `.env` is a **hostname or IP only**. Keep the default
`localhost` for a local demo; do not include the Next.js port in that value.

## A 90-second judge demo

For the clearest walkthrough, prepare one baseline session and one improved
submission rather than asking a judge to implement a change live.

1. Start a LogScope session and show the task ticket, multi-file tree, and
   browser terminal.
2. Run `make test` and `make bench` to establish the candidate baseline.
3. Open the **AI guide** tab to show the intentional coming-soon preview rather
   than an unconfigured chatbox.
4. Open the interviewer view to show the matching coming-soon status alongside
   the session controls and replay profile.
5. Reveal a prepared candidate improvement, rerun the benchmark, and submit.
6. In the interviewer view, show hidden-test totals, mission-level timings,
   the replay seed/profile, elapsed time, session pulse, and session log.

## Why these interviews feel like engineering work

Each pack contains a candidate repository with correct-but-inefficient paths,
visible tests, a benchmark workload, and intentional TODO extension seams.
The candidate can improve a coherent subset without rewriting the service.
Golden implementations and hidden grader assets are included in this hackathon
checkout so the evaluation is reviewable; in a real deployment they belong in
private grader storage.

The following labels are for interview calibration and follow-up; candidates
see product tickets and service vocabulary rather than a list of DSA topics.

| Pack | Scenario | Language | Interviewer-calibration concepts |
| --- | --- | --- | --- |
| `logscope-py` | Time-range log analysis | Python | Binary search, hashing, inverted index, heap, sliding window |
| `payfix-py` | Nightly payment reconciliation | Python | Hash maps, composite keys, grouping, memoization, heaps |
| `gateway-cache-ts` | Bounded API response cache | TypeScript | Hash maps, linked list, reverse index, fan-in, batching |
| `orderbook-py` | Exchange matching engine | Python | Price levels, hash maps, heaps, ordered lookup, deques |
| `dispatch-scheduler-ts` | Dependency-aware job scheduling | TypeScript | Graphs, in-degree, priority queues, min-heaps, interval search |
| `recofeed-py` | Recommendation feed assembly | Python | Hash sets/maps, top-K heaps, cache invalidation, batching |
| `route-mesh-ts` | API route resolution | TypeScript | Tries, prefix maps, prefix sums, binary search, cache invalidation |
| `inventory-search-py` | Commerce inventory search | Python | Tries, inverted indexes, hash joins, binary search, pagination |
| `risk-router-cpp` | Low-latency risk allocation | C++20 | Secondary indexes, prefix sums, binary search, versioned caches |

## Grading philosophy

Scout is deliberately not a "finish every TODO or fail" system. Each mission
has an equal default weight, and the automated score combines:

- correctness and invariant evidence (35%),
- improvement from the candidate baseline (35%),
- progress toward the golden benchmark (20%), and
- hidden-test confidence (10%).

The interviewer still evaluates code quality, prioritization, communication,
and trade-offs. As a guide, one meaningful mission is progress; two or three
well-executed missions is a strong interview; broad, correct improvement is
exceptional. See [INTERVIEW-FRAMEWORK.md](INTERVIEW-FRAMEWORK.md) for the full
challenge and scorecard contract.

## How the browser workflow works

```mermaid
flowchart LR
  C[Candidate browser] --> W[Scout web app<br/>Next.js]
  W --> S[Fresh session repository<br/>Docker container]
  S --> I[code-server IDE<br/>integrated terminal]
  C --> I
  W --> D[(SQLite<br/>sessions, reports, transcript)]
  C -. future AI guide .-> P[Deterministic policy gate]
  P -. guarded 

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 167 recognized source files, 221 KB.
- Anthropic (technology) — detected in the code
- C++ (language) — detected in the code
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vercel AI SDK (technology) — detected in the code
- Docker (technology) — claimed on Devpost, not found in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 258)

```
.env.example
.gitignore
app/admin/page.tsx
app/admin/sessions/[id]/page.tsx
app/api/admin/login/route.ts
app/api/candidate/[token]/chat/route.ts
app/api/candidate/[token]/route.ts
app/api/candidate/[token]/submit/route.ts
app/api/candidate/[token]/transcript/route.ts
app/api/challenges/route.ts
app/api/sessions/[id]/end/route.ts
app/api/sessions/[id]/report/route.ts
app/api/sessions/[id]/route.ts
app/api/sessions/[id]/toggle-agent/route.ts
app/api/sessions/route.ts
app/globals.css
app/layout.tsx
app/page.tsx
app/s/[token]/page.tsx
challenges/dispatch-scheduler-ts/agent/onboarding.md
challenges/dispatch-scheduler-ts/agent/symbols.json
challenges/dispatch-scheduler-ts/challenge.yaml
challenges/dispatch-scheduler-ts/golden/benchmarks/bench.mjs
challenges/dispatch-scheduler-ts/golden/README.md
challenges/dispatch-scheduler-ts/golden/src/priority-queue.mjs
challenges/dispatch-scheduler-ts/golden/src/scheduler.mjs
challenges/dispatch-scheduler-ts/grader/grade.sh
challenges/dispatch-scheduler-ts/grader/hidden/scheduler.invariant.mjs
challenges/dispatch-scheduler-ts/grader/mission-baselines.json
challenges/dispatch-scheduler-ts/repo/benchmarks/bench.mjs
challenges/dispatch-scheduler-ts/repo/Makefile
challenges/dispatch-scheduler-ts/repo/src/calendar-index.mjs
challenges/dispatch-scheduler-ts/repo/src/dispatch-state.mjs
challenges/dispatch-scheduler-ts/repo/src/ready-state.mjs
challenges/dispatch-scheduler-ts/repo/src/scheduler.mjs
challenges/dispatch-scheduler-ts/repo/src/worker-pool.mjs
challenges/dispatch-scheduler-ts/repo/TASK.md
challenges/dispatch-scheduler-ts/repo/tests/scheduler.test.mjs
challenges/dispatch-scheduler-ts/repo/TODO.md
challenges/dispatch-scheduler-ts/solution/solution.patch
challenges/dispatch-scheduler-ts/variants/calibration.json
challenges/dispatch-scheduler-ts/variants/manifest.json
challenges/gateway-cache-ts/agent/onboarding.md
challenges/gateway-cache-ts/agent/symbols.json
challenges/gateway-cache-ts/challenge.yaml
challenges/gateway-cache-ts/golden/README.md
challenges/gateway-cache-ts/golden/src/response-cache.mjs
challenges/gateway-cache-ts/golden/src/response-cache.ts
challenges/gateway-cache-ts/grader/grade.sh
challenges/gateway-cache-ts/grader/hidden/cache.invariant.mjs
challenges/gateway-cache-ts/repo/benchmarks/bench.mjs
challenges/gateway-cache-ts/repo/benchmarks/bench.ts
challenges/gateway-cache-ts/repo/Makefile
challenges/gateway-cache-ts/repo/src/cache-seams.mjs
challenges/gateway-cache-ts/repo/src/cache-seams.ts
challenges/gateway-cache-ts/repo/src/entry-seam.mjs
challenges/gateway-cache-ts/repo/src/request-seam.mjs
challenges/gateway-cache-ts/repo/src/response-cache.mjs
challenges/gateway-cache-ts/repo/src/response-cache.ts
challenges/gateway-cache-ts/repo/TASK.md
challenges/gateway-cache-ts/repo/tests/cache.test.mjs
challenges/gateway-cache-ts/repo/tests/cache.test.ts
challenges/gateway-cache-ts/repo/TODO.md
challenges/gateway-cache-ts/solution/solution.patch
challenges/gateway-cache-ts/variants/calibration.json
challenges/gateway-cache-ts/variants/manifest.json
challenges/inventory-search-py/agent/onboarding.md
challenges/inventory-search-py/agent/symbols.json
challenges/inventory-search-py/challenge.yaml
challenges/inventory-search-py/golden/catalog/__init__.py
challenges/inventory-search-py/golden/catalog/service.py
challenges/inventory-search-py/golden/README.md
challenges/inventory-search-py/grader/grade.sh
challenges/inventory-search-py/grader/hidden/test_invariants.py
challenges/inventory-search-py/grader/measure.py
challenges/inventory-search-py/repo/benchmarks/bench.py
challenges/inventory-search-py/repo/catalog/__init__.py
challenges/inventory-search-py/repo/catalog/browse_index.py
challenges/inventory-search-py/repo/catalog/identity_index.py
challenges/inventory-search-py/repo/catalog/indexes.py
challenges/inventory-search-py/repo/catalog/models.py
challenges/inventory-search-py/repo/catalog/service.py
challenges/inventory-search-py/repo/Makefile
challenges/inventory-search-py/repo/TASK.md
challenges/inventory-search-py/repo/tests/test_service.py
challenges/inventory-search-py/repo/TODO.md
challenges/inventory-search-py/solution/solution.patch
challenges/inventory-search-py/variants/calibration.json
challenges/inventory-search-py/variants/manifest.json
challenges/logscope-py/agent/onboarding.md
challenges/logscope-py/agent/symbols.json
challenges/logscope-py/challenge.yaml
challenges/logscope-py/golden/logscope/__init__.py
challenges/logscope-py/golden/logscope/ingestion.py
challenges/logscope-py/golden/logscope/query_engine.py
challenges/logscope-py/golden/logscope/retention.py
challenges/logscope-py/golden/README.md
challenges/logscope-py/grader/grade.sh
challenges/logscope-py/grader/hidden/test_invariants.py
challenges/logscope-py/repo/benchmarks/bench.py
challenges/logscope-py/repo/logscope/__init__.py
challenges/logscope-py/repo/logscope/compression.py
challenges/logscope-py/repo/logscope/ingestion.py
challenges/logscope-py/repo/logscope/models.py
challenges/logscope-py/repo/logscope/query_engine.py
challenges/logscope-py/repo/logscope/query_hints.py
challenges/logscope-py/repo/logscope/retention.py
challenges/logscope-py/repo/logscope/source_index.py
challenges/logscope-py/repo/logscope/time_index.py
challenges/logscope-py/repo/logscope/time_parser.py
challenges/logscope-py/repo/Makefile
challenges/logscope-py/repo/TASK.md
challenges/logscope-py/repo/tests/test_query_engine.py
challenges/logscope-py/repo/TODO.md
challenges/logscope-py/solution/solution.patch
challenges/logscope-py/variants/calibration.json
challenges/logscope-py/variants/manifest.json
challenges/orderbook-py/agent/onboarding.md
challenges/orderbook-py/agent/symbols.json
challenges/orderbook-py/challenge.yaml
[138 more files omitted for size]
```

### Dependencies

- package.json: @ai-sdk/anthropic@^1.2.12, @ai-sdk/openai-compatible@^0.2.14, @types/better-sqlite3@^7.6.12, @types/dockerode@^3.3.38, @types/js-yaml@^4.0.9, @types/node@^22.15.21, @types/react@^19.0.0, @types/react-dom@^19.0.0, ai@^4.3.16, better-sqlite3@^11.10.0, dockerode@^4.0.7, eslint@^9.39.5, eslint-config-next@^15.5.20, js-yaml@^4.1.0, nanoid@^5.1.5, next@^15.3.3, react@^19.0.0, react-dom@^19.0.0, typescript@^5.8.3, zod@^3.25.20

### Recent commits (newest first)

- Remove AGENTS.md from tracking
- Remove demo_recording_shotlist from cache and history
- Remove images from tracking, add to .gitignore; update README and homepage
- Update homepage and styles with platform screenshots and assets
- Remove documentation markdown files from tracking, add to .gitignore
- Update admin session page, session API, homepage, and global styles
- H10: Per-session replay variants — variant seed, manifests, calibration, benchmark integration, and tests
- Add supporting modules, TODO.md files, and latest refinements across all packs
- Update challenges with expanded repo/golden/grader files
- Complete all 7 handoff tasks
- Remove build artifacts and pycache files
- Initial commit

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

### challenges/gateway-cache-ts/agent/onboarding.md

```markdown
# Gateway project brief

Gateway provides a bounded response cache for a hot API path. `ResponseCache`
also owns expiry cleanup, header canonicalization, shared in-flight requests,
and tag-based removal. The repository includes replay-style benchmarks and
tests; run `make test` and `make bench` from the repository root.

```

### challenges/payfix-py/agent/onboarding.md

```markdown
# PayFix project brief

PayFix closes a nightly transaction export. Its reconciliation module contains
the matching, duplicate-reporting, currency, account-validation, and exception
queue stages used by the workflow. Preserve output ordering and public behavior.
Run `make test` and `make bench` from the repository root.

```

### package.json

```
{
  "name": "scout-interview-platform",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "images:build": "node scripts/build-images.mjs",
    "verify": "npm run typecheck && npm run lint && npm run build && node tests/test_grading.mjs && npm run variant-test && npm run test:public-host",
    "index-challenge": "node scripts/index-challenge.mjs",
    "guardrails": "node scripts/test-guardrails.mjs",
    "test:public-host": "node --no-warnings --experimental-strip-types tests/test_public_host.mjs",
    "variant-test": "node scripts/test_variants.mjs"
  },
  "dependencies": {
    "@ai-sdk/anthropic": "^1.2.12",
    "@ai-sdk/openai-compatible": "^0.2.14",
    "ai": "^4.3.16",
    "better-sqlite3": "^11.10.0",
    "dockerode": "^4.0.7",
    "js-yaml": "^4.1.0",
    "nanoid": "^5.1.5",
    "next": "^15.3.3",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "zod": "^3.25.20"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.12",
    "@types/dockerode": "^3.3.38",
    "@types/js-yaml": "^4.0.9",
    "@types/node": "^22.15.21",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "eslint": "^9.39.5",
    "eslint-config-next": "^15.5.20",
    "typescript": "^5.8.3"
  }
}

```

### images/challenge-cpp/Dockerfile

```
FROM debian:bookworm-slim AS code-server

ARG CODE_SERVER_VERSION=4.121.0
ARG TARGETARCH

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates curl tar \
    && rm -rf /var/lib/apt/lists/* \
    && architecture="${TARGETARCH:-$(dpkg --print-architecture)}" \
    && case "${architecture}" in \
      amd64) code_server_sha256="3860893f15376e5f984492c5c92e87c51975d67e3902410f422823d9f60e06af" ;; \
      arm64) code_server_sha256="de5101a3c1f86b3853431d6920dcf4daaca879f613dec9139496099e31baa569" ;; \
      *) echo "Unsupported code-server architecture: ${architecture}" >&2; exit 1 ;; \
    esac \
    && curl --fail --location --silent --show-error \
      "https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-${architecture}.tar.gz" \
      --output /tmp/code-server.tar.gz \
    && echo "${code_server_sha256}  /tmp/code-server.tar.gz" | sha256sum --check --status \
    && mkdir -p /opt/code-server \
    && tar -xzf /tmp/code-server.tar.gz --strip-components=1 -C /opt/code-server \
    && rm -rf /opt/code-server/lib/vscode/extensions/copilot \
      /opt/code-server/lib/vscode/extensions/github \
      /opt/code-server/lib/vscode/extensions/github-authentication \
      /opt/code-server/lib/vscode/extensions/mermaid-markdown-features \
      /opt/code-server/lib/vscode/extensions/microsoft-authentication \
    && rm /tmp/code-server.tar.gz

FROM debian:bookworm-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends bash ca-certificates g++ git make python3 \
    && ln -s /usr/bin/python3 /usr/local/bin/python \
    && useradd --create-home --shell /bin/bash candidate \
    && mkdir /workspace \
    && chown candidate:candidate /workspace \
    && rm -rf /var/lib/apt/lists/*

COPY --from=code-server /opt/code-server /opt/code-server

RUN ln -s /opt/code-server/bin/code-server /usr/local/bin/code-server

ENV HOME=/home/candidate
ENV PATH="/opt/code-server/bin:${PATH}"

USER candidate
WORKDIR /workspace
EXPOSE 8080

CMD ["code-server", "--bind-addr", "0.0.0.0:8080", "--auth", "password", "--disable-telemetry", "/workspace"]

```

### images/challenge-py/Dockerfile

```
ARG CODE_SERVER_VERSION=4.121.0

FROM debian:bookworm-slim AS code-server-download

ARG CODE_SERVER_VERSION
ARG TARGETARCH

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates curl tar \
    && architecture="${TARGETARCH:-$(dpkg --print-architecture)}" \
    && case "$architecture" in \
        amd64) code_server_sha256="3860893f15376e5f984492c5c92e87c51975d67e3902410f422823d9f60e06af" ;; \
        arm64) code_server_sha256="de5101a3c1f86b3853431d6920dcf4daaca879f613dec9139496099e31baa569" ;; \
        *) echo "Unsupported architecture: $architecture" >&2; exit 1 ;; \
    esac \
    && curl --fail --location --silent --show-error --retry 3 \
        --output /tmp/code-server.tar.gz \
        "https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-${architecture}.tar.gz" \
    && echo "${code_server_sha256}  /tmp/code-server.tar.gz" | sha256sum --check --status \
    && mkdir -p /opt/code-server \
    && tar --extract --gzip --file /tmp/code-server.tar.gz --strip-components=1 --directory /opt/code-server \
    && rm -rf /opt/code-server/lib/vscode/extensions/copilot \
      /opt/code-server/lib/vscode/extensions/github \
      /opt/code-server/lib/vscode/extensions/github-authentication \
      /opt/code-server/lib/vscode/extensions/mermaid-markdown-features \
      /opt/code-server/lib/vscode/extensions/microsoft-authentication \
    && rm -rf /var/lib/apt/lists/* /tmp/code-server.tar.gz

FROM python:3.12-slim-bookworm

RUN apt-get update \
    && apt-get install -y --no-install-recommends bash ca-certificates git libstdc++6 make \
    && rm -rf /var/lib/apt/lists/* \
    && useradd --create-home --shell /bin/bash candidate \
    && mkdir -p /workspace \
    && chown candidate:candidate /workspace

COPY --from=code-server-download /opt/code-server /opt/code-server

RUN ln -s /opt/code-server/bin/code-server /usr/local/bin/code-server

ENV HOME=/home/candidate
ENV PATH="/opt/code-server/bin:${PATH}"

USER candidate
WORKDIR /workspace
EXPOSE 8080
CMD ["code-server", "--bind-addr", "0.0.0.0:8080", "--auth", "password", "--disable-telemetry", "/workspace"]

```

### images/challenge-ts/Dockerfile

```
FROM debian:bookworm-slim AS code-server

ARG CODE_SERVER_VERSION=4.121.0
ARG TARGETARCH

RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends ca-certificates curl tar; \
    rm -rf /var/lib/apt/lists/*; \
    architecture="${TARGETARCH:-$(dpkg --print-architecture)}"; \
    case "$architecture" in \
      amd64) checksum="3860893f15376e5f984492c5c92e87c51975d67e3902410f422823d9f60e06af" ;; \
      arm64) checksum="de5101a3c1f86b3853431d6920dcf4daaca879f613dec9139496099e31baa569" ;; \
      *) echo "Unsupported code-server architecture: $architecture" >&2; exit 1 ;; \
    esac; \
    archive="code-server-${CODE_SERVER_VERSION}-linux-${architecture}.tar.gz"; \
    curl --fail --location --silent --show-error \
      "https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/${archive}" \
      --output "/tmp/${archive}"; \
    echo "${checksum}  /tmp/${archive}" | sha256sum --check --status; \
    tar --extract --gzip --file "/tmp/${archive}" --directory /opt; \
    mv "/opt/code-server-${CODE_SERVER_VERSION}-linux-${architecture}" /opt/code-server; \
    rm -rf /opt/code-server/lib/vscode/extensions/copilot \
      /opt/code-server/lib/vscode/extensions/github \
      /opt/code-server/lib/vscode/extensions/github-authentication \
      /opt/code-server/lib/vscode/extensions/mermaid-markdown-features \
      /opt/code-server/lib/vscode/extensions/microsoft-authentication; \
    rm "/tmp/${archive}"

FROM node:22-bookworm-slim

COPY --from=code-server /opt/code-server /opt/code-server

RUN ln -s /opt/code-server/bin/code-server /usr/local/bin/code-server

RUN apt-get update \
    && apt-get install -y --no-install-recommends bash ca-certificates git libstdc++6 make python3 \
    && ln -s /usr/bin/python3 /usr/local/bin/python \
    && useradd --create-home --shell /bin/bash candidate \
    && mkdir -p /workspace \
    && chown candidate:candidate /workspace \
    && rm -rf /var/lib/apt/lists/*

ENV HOME=/home/candidate
ENV PATH="/opt/code-server/bin:${PATH}"

USER candidate
WORKDIR /workspace
EXPOSE 8080
CMD ["code-server", "--bind-addr", "0.0.0.0:8080", "--auth", "password", "--disable-telemetry", "/workspace"]

```

### app/layout.tsx

```typescript
import "./globals.css";

export const metadata = {
  title: "Scout Interviews",
  description: "Fair, evidence-based live coding interviews"
};

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

```

### app/page.tsx

```typescript
import Image from "next/image"
import Link from "next/link"

const interviewSignals = [
  ["Explore", "Read a real service, trace the important paths, and understand the constraints before changing code."],
  ["Prioritize", "Choose the improvement with the strongest likely impact instead of chasing every available task."],
  ["Improve", "Make a focused change while preserving behavior and existing contracts."],
  ["Prove", "Use tests and benchmarks to show that the repository is both correct and meaningfully better."],
]

const evidence = [
  ["Live workspace", "A browser-based VS Code session where candidates investigate and implement in a real repository."],
  ["Measured progress", "Tests, benchmarks, and mission-level progress make partial improvements visible."],
  ["Interview evidence", "Review the work, elapsed time, scorecard, and the trade-offs behind the change."],
]

export default function HomePage() {
  return (
    <main className="shell landing-shell story-shell">
      <nav className="topbar story-nav">
        <div><div className="brand">Scout<span>.</span></div><div className="muted">Repository interview platform</div></div>
        <div className="story-nav-actions"><a href="#problem">Why Scout</a><a href="#format">Interview format</a><Link className="button" href="/admin">Open interviewer console</Link></div>
      </nav>

      <section className="story-hero">
        <div className="story-hero-art"><Image src="/demo/traditional-interview-pressure.png" alt="Engineer facing an empty editor and countdown clock" fill priority sizes="(max-width: 1240px) 100vw, 1180px" /></div>
        <div className="story-hero-shade" />
        <div className="story-hero-copy">
          <div className="eyebrow">The hiring-signal problem</div>
          <h1>A blank editor cannot show how an engineer works.</h1>
          <p>Traditional coding rounds reward puzzle recall under artificial pressure. They rarely reveal how a candidate understands an unfamiliar system, chooses a bottleneck, or proves an improvement is safe.</p>
          <div className="actions"><a className="button primary" href="#problem">See the interview problem</a><Link className="button story-hero-button" href="/admin">Run the live demo</Link></div>
        </div>
        <div className="story-hero-signals"><span>Real repository</span><span>Multiple improvement paths</span><span>Evidence-based review</span></div>
      </section>

      <section className="story-section story-problem" id="problem">
        <div className="story-section-intro"><div className="eyebrow">Why traditional rounds break down</div><h2>One isolated answer is a weak proxy for engineering judgment.</h2><p className="muted">The problem is not the candidate. It is an assessment that removes context, encourages pattern recall, and asks one answer to carry too much signal.</p></div>
        <div className="story-problem-grid">
          <article><span>01</span><h3>No system context</h3><p>A single prompt cannot show how someone reads a service, maps constraints, or finds the real hot path.</p></article>
          <article><span>02</span><h3>Recall over judgment</h3><p>Recognizing a trick is different from deciding what improvement is worth making in an existing system.</p></article>
          <article><span>03</span><h3>One-screen AI answers</h3><p>When a hidden tool can answer one prompt, the interview needs stronger evidence than the final code alone.</p></article>
        </div>
      </section>

      <section className="story-section story-split story-ai-section">
        <div className="story-visual-frame"><Image src="/demo/ai-assessment-dilemma.png" alt="Conceptual illustration of the AI assessment dilemma" width={1600} height={900} sizes="(max-width: 800px) 100vw, 56vw" /></div>
        <div className="story-copy-block"><div className="eyebrow">AI exposed the flaw</div><h2>Surveillance is not a better interview.</h2><p>One-screen answer tools make a puzzle-only assessment harder to trust. Adding more browser locks or camera checks does not reveal how someone thinks inside a real system.</p><p>Scout changes the format instead: candidates work in context, choose a path, and demonstrate impact with evidence.</p></div>
      </section>

      <section className="story-section story-split story-solution" id="format">
        <div className="story-copy-block"><div className="eyebrow">The Scout format</div><h2>Start with a real system. Let engineering judgment become visible.</h2><p>Scout is a growing library of production-shaped interview repositories. Every repository includes a realistic ticket, multiple files, tests, benchmarks, and several independently measurable opportunities to improve the system.</p><p>There is no prescribed solution and no expectation to finish everything. A strong candidate identifies a high-value opportunity, makes a focused change, and proves it worked.</p><Link className="button primary" href="/admin">Browse interview environments</Link></div>
        <div className="story-visual-frame story-solution-art"><Image src="/demo/repository-engineering-judgment.png" alt="Conceptual illustration of navigating and improving a repository" width={1600} height={900} sizes="(max-width: 800px) 100vw, 56vw" /></div>
      </section>

      <section className="story-section story-process">
        <div className="story-section-intro"><div className="eyebrow">A better interview signal</div><h2>One repository. Several credible ways to demonstrate impact.</h2></div>
        <div className="story-process-grid">{interviewSignals.map(([title, description], index) => <article key={title}><span>{String(index + 1).padStart(2, "0")}</span><h3>{title}</h3><p className="muted">{description}</p></article>)}</div>
      </section>

      <section className="story-section story-evidence">
        <div className="story-section-intro"><div className="eyebrow">The live interview</div><h2>Review evidence, not just the final answer.</h2><p className="muted
[truncated — 1412 more characters]
```

### app/admin/page.tsx

```typescript
"use client"

import Link from "next/link"
import { useCallback, useEffect, useState } from "react"

type Challenge = {
  slug: string
  title: string
  language: string
  level: string
  domain: string
  summary: string
  mission_count: number
  dsa_topics: string[]
  repo_shape: string
  intentional_todos: number
}

type Session = {
  id: string
  candidate_name: string
  title: string
  status: string
  created_at: string
  ended_at: string | null
}

type CreatedSession = { candidateUrl: string; idePassword: string; id: string; title: string }

function elapsedLabel(startedAt: string, now: number, endedAt: string | null) {
  const total = Math.max(0, Math.floor(((endedAt ? new Date(endedAt).getTime() : now) - new Date(startedAt).getTime()) / 1000))
  const hours = Math.floor(total / 3600)
  const minutes = Math.floor((total % 3600) / 60)
  const seconds = total % 60
  return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`
}

export default function AdminPage() {
  const [challenges, setChallenges] = useState<Challenge[]>([])
  const [sessions, setSessions] = useState<Session[]>([])
  const [busySlug, setBusySlug] = useState("")
  const [message, setMessage] = useState("")
  const [created, setCreated] = useState<CreatedSession | null>(null)
  const [copied, setCopied] = useState("")
  const [now, setNow] = useState(Date.now())

  const load = useCallback(async () => {
    const [challengeResponse, sessionResponse] = await Promise.all([fetch("/api/challenges"), fetch("/api/sessions")])
    if (challengeResponse.ok) setChallenges(await challengeResponse.json())
    if (sessionResponse.ok) setSessions(await sessionResponse.json())
  }, [])

  useEffect(() => {
    void load()
    const timer = setInterval(() => setNow(Date.now()), 1000)
    return () => clearInterval(timer)
  }, [load])

  async function copyText(value: string, label: string) {
    try {
      await navigator.clipboard.writeText(value)
      setCopied(`${label} copied`)
      window.setTimeout(() => setCopied(""), 1800)
    } catch {
      setCopied(`Could not copy ${label.toLowerCase()}`)
    }
  }

  async function startSession(challenge: Challenge) {
    setBusySlug(challenge.slug)
    setMessage(`Starting ${challenge.title}...`)
    setCreated(null)
    const response = await fetch("/api/sessions", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ challenge_slug: challenge.slug, candidate_name: "Demo candidate" }),
    })
    const data = await response.json()
    if (!response.ok) {
      setMessage(data.error || "Could not create session")
      setBusySlug("")
      return
    }
    setCreated({ ...data, title: challenge.title })
    setMessage("")
    setBusySlug("")
    await load()
  }

  return <main className="shell">
    <div className="topbar">
      <div><div className="brand">Scout<span>.</span></div><div className="muted">Repository interview demo</div></div>
      <Link href="/" className="button">Home</Link>
    </div>

    <section className="hero" style={{ paddingTop: 28 }}>
      <div className="eyebrow">Pick a production-shaped task</div>
      <h1>Browse a codebase. Start a session.</h1>
      <p>Each task is an open-ended repository investigation with five independently measurable opportunities. Sessions stay active until you end them.</p>
      {message && <div className="notice">{message}</div>}
    </section>

    {created && <section className="session-ready-card" aria-live="polite">
      <div className="session-ready-heading">
        <div>
          <div className="eyebrow">Workspace ready</div>
          <h2>{created.title}</h2>
          <p className="muted">Share the workspace link and IDE password with the candidate. They can open the browser IDE immediately.</p>
        </div>
        <div className="actions" style={{ marginTop: 0 }}>
          <a className="button primary" href={created.candidateUrl} target="_blank" rel="noreferrer">Open candidate workspace</a>
          <Link className="button" href={`/admin/sessions/${created.id}`}>Open interviewer view</Link>
          <button className="button" onClick={() => setCreated(null)}>Dismiss</button>
        </div>
      </div>
      <div className="workspace-credentials">
        <div className="credential">
          <span>Candidate workspace link</span>
          <div className="credential-value">
            <a className="code credential-link" href={created.candidateUrl} target="_blank" rel="noreferrer">{created.candidateUrl}</a>
            <button className="button" onClick={() => void copyText(created.candidateUrl, "Link")}>Copy link</button>
          </div>
        </div>
        <div className="credential">
          <span>IDE password</span>
          <div className="credential-value">
            <code className="code credential-password">{created.idePassword}</code>
            <button className="button" onClick={() => void copyText(created.idePassword, "Password")}>Copy password</button>
          </div>
        </div>
      </div>
      {copied && <div className="session-ready-feedback" role="status">{copied}</div>}
    </section>}

    <section className="card">
      <div className="split"><div><div className="eyebrow">Task catalog</div><h2>{challenges.length} interview environments</h2></div><button className="button" onClick={() => void load()}>Refresh list</button></div>
      <div className="portfolio-grid">{challenges.map((challenge) => <div className="portfolio-item" key={challenge.slug}>
        <div className="eyebrow">{challenge.domain} · {challenge.repo_shape}</div>
        <h3>{challenge.title}</h3>
        <p className="muted">{challenge.summary}</p>
        <span className="pill">{challenge.mission_count} missions · {challenge.language}</span>
        <p className="muted" style={{ marginTop: 10, fontSize: 13 }}>DSA coverage: {challenge.dsa_topics.join(", ")} · {challenge.intentional_todos} implementation seams</p>
 
[truncated — 1120 more characters]
```

### app/api/sessions/route.ts

```typescript
import { NextResponse } from "next/server";
import { getDb } from "@/lib/db";
import { createSession, sweepExpiredSessions } from "@/lib/sessions";

export async function GET() {
  await sweepExpiredSessions();
  const rows = getDb().prepare(`SELECT s.*, c.title FROM sessions s JOIN challenges c ON c.slug=s.challenge_slug ORDER BY s.created_at DESC`).all();
  return NextResponse.json(rows);
}

export async function POST(request: Request) {
  try {
    const body = await request.json() as { challenge_slug?: string; candidate_name?: string; variant_seed?: number };
    const result = await createSession({
      challengeSlug: body.challenge_slug || "",
      candidateName: body.candidate_name || "Candidate",
      variantSeed: body.variant_seed,
      candidateOrigin: new URL(request.url).origin,
    });
    return NextResponse.json(result, { status: 201 });
  } catch (error) {
    return NextResponse.json({ error: error instanceof Error ? error.message : "Could not create session" }, { status: 500 });
  }
}

```

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