# Project export: InOrdo

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: Turn project updates into evidence-backed, human-approved recovery actions.
- Devpost: https://devpost.com/software/chimera-i4oz8d
- GitHub: https://github.com/Chi944/InOrdo
- Demo: https://inordo.vercel.app/
- Video: https://www.youtube.com/embed/eDPB6wtkFrM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Chi944 (88 commits), dres2525 (1 commits)

## Devpost submission (written by the team)

### Overview

Evidence → deterministic impact → recovery proposal → human approval → reversible history. Live application: https://inordo.vercel.app Demo video: https://youtu.be/eDPB6wtkFrM Source code: https://github.com/Chi944/InOrdo Track: Work & Productivity

### Inspiration

A project update rarely changes only one record. If a summit venue moves by two weeks, the event date is obvious—but speaker confirmations, catering, programme deadlines, travel, accessibility arrangements, signage, and briefing materials may all become stale. Small teams often reconstruct that chain manually, under time pressure, with the source fact, inferred impact, and resulting edits scattered across different tools. We built InOrdo to make that chain of reaction visible and controllable.

### What it does

InOrdo turns an unstructured project update into a reviewable workflow: Preserve the evidence. A typed or pasted update is stored as the source of truth. Draft a candidate change. GPT-5.6 extracts a bounded structured interpretation while keeping source fact and model inference visibly separate. Calculate downstream impact. Pure TypeScript traverses explicit project dependencies deterministically, returning direct and indirect effects with readable paths. Draft recovery actions. GPT-5.6 receives the validated change and deterministic paths, then proposes a bounded set of recovery actions. Require human approval. Every proposal remains inert until an authorized person selects specific allowlisted actions. Preserve history and undo safely. Applied changes record before/after state and actor attribution. Supported operations can be reversed through a linked compensating operation rather than erasing history. The submitted synthetic scenario is the Regional Climate Action Summit 2026: 24 active project records and 26 explicit dependencies. A venue update moves the summit from 12 September to 26 September 2026, allowing the reviewer to inspect its direct and multi-hop consequences. What makes InOrdo different InOrdo is not trying to replace broad work-management platforms. Instead, it focuses on a narrower trust boundary that begins when new evidence invalidates part of an existing plan: Evidence → structured change → deterministic reach → inert proposal → human approval → reversible history The model interprets and drafts, but it never decides graph reach, authorizes itself, or directly mutates project data. This combination of preserved evidence, explainable dependency paths, selective approval, and compensating undo is the core product idea.

### How we built it

InOrdo is a Next.js 16 App Router application using React 19, TypeScript, Tailwind CSS, Supabase Postgres and Auth, and the OpenAI Responses API. The GPT-5.6 boundary is server-only. It has no tools or write authority. Strict schemas and application post-validation check identifiers, fields, values, dates, evidence spans, confidence, impact coverage, and allowlisted action types before model output can enter the review workflow. Dependency reach is deliberately not delegated to the model. A pure TypeScript traversal handles cycles, duplicate edges, depth limits, deterministic ordering, and stable shortest paths over one project’s active records. Supabase row-level security and server-side authorization enforce workspace and role boundaries. Before applying selected actions, the server rechecks proposal ownership, current item versions, action eligibility, human input, and idempotency. Supported undo operations recheck recorded after-state before creating a compensating transaction. Codex accelerated concrete work packages across the repository foundation, schema and RLS design, deterministic graph engine, strict model contracts, approval and undo services, accessible interface, automated tests, security review, and release evidence. Deston and Andres retained responsibility for product direction, design decisions, credentials, provider spending, approvals, and release authority.

### Challenges we ran into

Making AI useful without giving it authority The model needed enough context to interpret an update and draft useful actions, but not enough authority to traverse the graph, approve work, or mutate data. We enforced that separation in server and database contracts rather than relying on interface copy. Keeping graph results reproducible Cycles, fan-out, duplicate edges, archived records, and multiple possible routes can make impact analysis ambiguous. We implemented bounded deterministic traversal with stable ordering and a best-depth policy so the same graph produces the same explainable paths. Making undo honest A button labeled “undo” is not sufficient. Safe reversal requires recorded before/after state, version checks, and a linked compensating operation. If state has become stale or the action is unsupported, InOrdo fails closed. Recording a real demo without exposing unlimited API usage We authorized exactly one purpose-specific Production GPT-5.6 run. After capturing and verifying the saved result, the recording key was revoked and Production returned to disabled analysis mode. Judges can inspect the genuine persisted result without being able to initiate paid requests. Accomplishments that we are proud of A coherent evidence-to-recovery workflow rather than an isolated AI feature. One verified Production GPT-5.6 journey from preserved evidence through direct and indirect impact, recovery proposal, explicit approval, apply, linked history, and compensating undo. A deterministic dependency engine with readable paths and coverage for chains, fan-out, fan-in, cycles, duplicates, archived items, maximum depth, ordering, and shortest-path behavior. Server-enforced human approval and role boundaries; model output never directly mutates data. A synthetic workspace containing six project-record types, 24 active records, and 26 explicit dependencies. 514 passing unit and component tests across 64 files, two guarded Playwright Chromium journeys, a successful production build, and zero audited production dependency vulnerabilities. A responsive, keyboard-accessible judging experience with an intentionally read-only viewer account.

### What we learned

Structured output is only the beginning of a safe model boundary. Canonical-state validation, evidence checks, authorization, and deterministic application logic still matter. Explainability becomes much stronger when graph paths—not generated prose—are authoritative for dependency reach. “Human in the loop” must be a real server-side state transition. A checkbox alone does not provide control. Finally, honest limitations improve a demo. Disabled controls and explicit read-only states are better than implying that unfinished or unavailable capabilities work.

### What's next

Add proposal correction and rejection workflows. Expand ordinary workspace creation, invitations, and project switching. Add authenticated end-to-end coverage and stronger operational monitoring. Introduce reviewed text/Markdown and CSV ingestion before considering broader connectors. Explore email, Slack, Teams, Google Drive, voice, and URL ingestion only after the standalone evidence-and-approval workflow remains reliable. Extend reversible operation support while preserving the same fail-closed contracts. Testing the submitted build Judges receive a dedicated read-only account through Devpost’s private testing instructions. They can navigate the synthetic workspace, inspect records and dependencies, open saved evidence, follow deterministic impact paths, review the persisted GPT-5.6 proposal, and inspect apply/undo history. New paid analysis and all project mutations are disabled for the judge account. The visible model result is the genuine saved Production result shown in the demo video. Current limitations are intentional and visible: only the synthetic summit workspace is provisioned; ordinary-project creation is an informational preview; and file, CSV, URL, voice, email, Slack, Teams, and Google Drive imports are not implemented.

## README (from the GitHub repository)

# InOrdo

<div align="center">

**Turn an unstructured project update into evidence-backed impacts and recovery actions—without giving the model permission to change the plan.**

[![CI](https://github.com/Chi944/InOrdo/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Chi944/InOrdo/actions/workflows/ci.yml)
![Node.js 22](https://img.shields.io/badge/Node.js-22-339933?logo=nodedotjs&logoColor=white)
![Next.js 16](https://img.shields.io/badge/Next.js-16-000000?logo=nextdotjs&logoColor=white)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

[Open the application](https://inordo.vercel.app) · [Read the architecture](docs/architecture.md) · [Follow the demo](docs/demo-scenario.md) · [Review release evidence](docs/release-evidence.md)

<sub>OpenAI Build Week · Work & Productivity · Built for small teams</sub>

</div>

InOrdo gives a team one reviewable path from new evidence to a safe project response. It preserves the source, uses a bounded server-side model route for interpretation and drafting, computes dependency reach in deterministic TypeScript, and requires an authorized person to approve each internal action. Applied operations are attributable and reversible only when the recorded contract says they are.

> [!IMPORTANT]
> **The canonical deployment is [inordo.vercel.app](https://inordo.vercel.app).** It serves the release built by direct Vercel CLI deployment from a clean worktree at reviewed `main` commit [`4f54cc1eec37d49aa6b1da6e0dafbc6f7d738d03`](https://github.com/Chi944/InOrdo/commit/4f54cc1eec37d49aa6b1da6e0dafbc6f7d738d03). Post-recording Production deployment `dpl_BW4kvr2zMUNkwv46XEeMMFRJeisJ` is `READY` at immutable [inordo-caheq8v2h-chi944s-projects.vercel.app](https://inordo-caheq8v2h-chi944s-projects.vercel.app). Vercel Authentication remains enabled for Preview deployments only.
>
> Production is deliberately back in `ANALYSIS_MODE=disabled`; `/api/health` is `ready` with analysis disabled. Exactly one bounded GPT-5.6 Production run succeeded, and its genuine saved evidence, direct and indirect impact paths, recovery proposal, selected internal date action, explicit human response, successful apply, linked history, and compensating undo remain viewable. Before that run, an older duplicate active InOrdo provider key was discovered and revoked. After the playable capture, the fresh recording key was revoked, zero active InOrdo keys remained, the Vercel `OPENAI_API_KEY` was removed, and local `.env.recording.local` was deleted. New paid analysis is disabled.

## Product interface

These captures show the implemented product interface from an optimized build. They are not a live-provider response or proof of an authenticated production session.

| Landing and change trace | Responsibility-separated workflow |
| --- | --- |
| ![InOrdo landing page showing the Control project change without the chain reaction headline and an illustrative venue-date trace.](docs/assets/inordo-landing.jpg) | ![InOrdo workflow showing evidence, deterministic impact, recovery drafting, and human approval as distinct steps.](docs/assets/inordo-workflow.jpg) |

The completed external demo video uses genuine Production footage. These two checked-in images remain illustrative optimized-build captures and are not themselves proof of a live GPT-5.6 response; synthetic or intercepted test data must never be presented as one.

## Contents

- [Why InOrdo](#why-inordo)
- [Where InOrdo fits](#where-inordo-fits)
- [How it works](#how-it-works)
- [What is implemented](#what-is-implemented)
- [Quick start](#quick-start)
- [Configuration](#configuration)
- [Local development versus Vercel](#local-development-versus-vercel)
- [Architecture and safety contracts](#architecture-and-safety-contracts)
- [Testing and release](#testing-and-release)
- [Demo scenario](#demo-scenario)
- [Limitations and non-goals](#limitations-and-non-goals)
- [Documentation](#documentation)
- [Contributing](#contributing)
- [Submission handoff](#submission-handoff)
- [License](#license)

## Why InOrdo

A venue change, shifted deadline, or revised decision rarely affects one record. It can invalidate tasks, milestones, risks, and shared artifacts several steps downstream. Small teams often reconstruct that chain manually, under time pressure, with the source fact, inferred impact, and resulting edits scattered across different tools.

InOrdo keeps those responsibilities visible and separate:

**evidence → impact → proposal → approval → history and undo**

- Preserve the raw update before interpreting it.
- Distinguish source fact from model inference.
- Derive downstream reach from explicit, inspectable dependencies.
- Show a full path for every direct or indirect impact.
- Keep drafted actions inert until selective human approval.
- Apply allowed internal changes atomically and record who did what.
- Undo only when the entire operation is supported and still safe to reverse.

## Where InOrdo fits

InOrdo is not intended to replace a general-purpose work-management suite. The comparison below reflects each vendor's own published product emphasis; it does not claim that another product lacks adjacent capabilities.

| Product | Current first-party emphasis |
| --- | --- |
| [Asana](https://asana.com/features) | Organization-wide project and work management, goals, reporting, resources, workflows, automation, and human-agent collaboration. |
| [Jira](https://www.atlassian.com/software/jira/features) | Planning, tracking, dependency management, collaboration, reporting, customizable workflows, integrations, automation, and AI assistance. |
| [Linear](https://linear.app/) | Product-development planning and execution for teams and agents, including intake, projects, issues, and updates; its documentation also describes explicit [project dependency views](https://linear.app/docs/project-dependencies). |
| [monday work management](https://support.monday.com/hc/en-us/articles/115005305649-Get-started-with-monday-work-management) | Board-based project and operational work management with views, dashboards, automations, integrations, and embedded AI workflows. |

InOrdo's Build Week P0 explores a narrower control loop: preserve an unstructured update as inspectable evidence, validate a structured candidate change, compute downstream reach with deterministic code, let the model draft inert recovery options, and require an authorized human to select allowlisted operations. Applied field updates retain actor-attributed history and support compensating undo only when the recorded contract remains safe.

The distinction is therefore an explicit **evidence → deterministic impact → proposal → human approval → reversible history** trust boundary—not a larger feature list or a claim of exclusive capability. InOrdo currently lacks the connector ecosystem, ordinary-workspace provisioning, and platform breadth of the products above.

## How it works

```mermaid
flowchart LR
    E["Unstructured update"] --> S["Immutable evidence"]
    S --> X["Bounded model extraction"]
    X --> V["Strict schema and evidence validation"]
    V --> G["Deterministic graph traversal"]
    G --> D["Bounded recovery draft"]
    D --> H["Human selective approval"]
    H --> O["Atomic allowlisted operation"]
    O --> A["Actor-attributed history"]
    A --> U["Compensating undo when safe"]

    classDef model fill:#fff7ed,stroke:#c2410c,color:#431407;
    classDef deterministic fill:#eff6ff,stroke:#1d4ed8,color:#172554;
    classDef human fill:#f0fdf4,stroke:#15803d,color:#052e16;
    class X,D model;
    class S,V,G,O,A,U deterministic;
    class H human;
```

The reviewed provider policy supports an exceptional one-use GPT-5.6 recording route, a separately capped Vercel AI Gateway route using the open-weight GPT-OSS model, or a disabled state. A model has no tools, never traverses the graph, never authorizes a user, and never mutates data. Application and database code own canonical state, authori

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 233 recognized source files, 2193 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
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- PostgreSQL (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 (120 of 247)

```
.env.example
.github/ISSUE_TEMPLATE/work-item.yml
.github/pull_request_template.md
.github/workflows/ci.yml
.gitignore
.nvmrc
AGENTS.md
docs/architecture.md
docs/backlog.md
docs/codex-log.md
docs/demo-scenario.md
docs/demo-user-setup.md
docs/deployment-runbook.md
docs/devpost-handoff.md
docs/local-run-and-test.md
docs/product-brief.md
docs/prompt-7-readiness.md
docs/qa-checklist.md
docs/release-evidence.md
docs/rollback-plan.md
docs/security-review.md
docs/submission-checklist.md
docs/submission-copy.md
docs/superpowers/plans/2026-07-18-project-records-dependency-engine.md
docs/superpowers/plans/2026-07-21-analysis-access-and-fallback.md
docs/superpowers/plans/2026-07-21-judge-project-experience.md
docs/superpowers/plans/2026-07-21-submission-production-release.md
docs/superpowers/specs/2026-07-21-submission-production-design.md
docs/video-production/andres-voiceover.md
docs/video-production/deston-voiceover.md
docs/video-production/production-runbook.md
docs/video-production/thumbnail-brief.md
docs/video-script.md
eslint.config.mjs
LICENSE
next.config.ts
package.json
playwright.config.ts
postcss.config.mjs
README.md
scripts/applied-migration-paths.mjs
scripts/revert-plan.mjs
scripts/verify-migration-parity.mjs
src/app/__e2e__/core-demo/page.tsx
src/app/%5F%5Fe2e%5F%5F/core-demo/page.tsx
src/app/api/health/route.test.ts
src/app/api/health/route.ts
src/app/api/projects/[projectId]/analyze/route.ts
src/app/api/projects/[projectId]/demo/reset/route.ts
src/app/api/projects/[projectId]/operations/[operationId]/undo/route.ts
src/app/api/projects/[projectId]/operations/route.ts
src/app/api/projects/[projectId]/proposals/[proposalId]/apply/route.ts
src/app/app/analysis-provider-label.test.ts
src/app/app/analysis-provider-label.ts
src/app/app/decisions/page.tsx
src/app/app/demo-reset-control.test.tsx
src/app/app/demo-reset-control.tsx
src/app/app/demo-targets.ts
src/app/app/dependencies/dependency-view.test.tsx
src/app/app/dependencies/dependency-view.tsx
src/app/app/dependencies/page.tsx
src/app/app/error.tsx
src/app/app/focused-records.test.tsx
src/app/app/focused-records.tsx
src/app/app/guided-demo-callout.tsx
src/app/app/impact-workflow-types.ts
src/app/app/impact-workflow.test.tsx
src/app/app/impact-workflow.tsx
src/app/app/items/[itemId]/page.tsx
src/app/app/items/page.tsx
src/app/app/items/project-item-editor.test.tsx
src/app/app/items/project-item-editor.tsx
src/app/app/items/project-items-view.test.tsx
src/app/app/items/project-items-view.tsx
src/app/app/layout.tsx
src/app/app/loading.tsx
src/app/app/not-found.tsx
src/app/app/operation-idempotency.test.ts
src/app/app/operation-idempotency.ts
src/app/app/page.tsx
src/app/app/project-navigation.test.tsx
src/app/app/project-navigation.tsx
src/app/app/project-record-action-state.ts
src/app/app/project-record-actions.test.ts
src/app/app/project-record-actions.ts
src/app/app/project-view-data.ts
src/app/app/projects/ordinary/ordinary-project-notice.test.tsx
src/app/app/projects/ordinary/ordinary-project-notice.tsx
src/app/app/projects/ordinary/page.tsx
src/app/app/projects/page.tsx
src/app/app/projects/project-catalog.test.tsx
src/app/app/projects/project-catalog.tsx
src/app/app/recovery-action-review.test.tsx
src/app/app/recovery-action-review.tsx
src/app/app/risks/page.tsx
src/app/app/source-update-form.test.tsx
src/app/app/source-update-form.tsx
src/app/app/workflow-overview.test.ts
src/app/app/workflow-overview.ts
src/app/globals.css
src/app/layout.tsx
src/app/login/actions.test.ts
src/app/login/actions.ts
src/app/login/login-form.tsx
src/app/login/page.tsx
src/app/page.test.tsx
src/app/page.tsx
src/components/skip-link.test.tsx
src/components/skip-link.tsx
src/features/analysis/claim-lease.test.ts
src/features/analysis/context.test.ts
src/features/analysis/context.ts
src/features/analysis/errors.test.ts
src/features/analysis/errors.ts
src/features/analysis/gateway-adapter.test.ts
src/features/analysis/gateway-adapter.ts
src/features/analysis/model-context.test.ts
src/features/analysis/model-context.ts
src/features/analysis/model-schemas.test.ts
src/features/analysis/model-schemas.ts
[127 more files omitted for size]
```

### Dependencies

- package.json: @playwright/test@^1.61.1, @supabase/ssr@^0.12.3, @supabase/supabase-js@^2.110.7, @tailwindcss/postcss@^4, @testing-library/jest-dom@^6.9.1, @testing-library/react@^16.3.2, @testing-library/user-event@^14.6.1, @types/node@^22.20.1, @types/react@^19, @types/react-dom@^19, clsx@^2.1.1, date-fns@^4.4.0, eslint@^9, eslint-config-next@16.2.10, jsdom@^29.1.1, lucide-react@^1.25.0, next@16.2.10, openai@^6.48.0, react@19.2.4, react-dom@19.2.4, supabase@2.109.1, tailwind-merge@^3.6.0, tailwindcss@^4, typescript@^5, vitest@^4.1.10, zod@^4.4.3

### Recent commits (newest first)

- Merge pull request #26 from Chi944/codex/22-submission-finalization
- docs: clarify final link gate
- docs: record public demo video
- docs: finalize submission handoff
- Merge pull request #25 from Chi944/codex/21-final-polish
- docs: finalize submission polish
- Merge pull request #24 from Chi944/codex/20-submission-release
- docs: finalize submission release evidence
- docs: record production release controls
- Merge pull request #23 from Chi944/codex/release-evidence-privacy
- docs: correct release privacy metadata
- Merge pull request #22 from Chi944/codex/19-submission-production
- docs: verify contained deployment URLs
- docs: align production release controls
- docs: clarify no-retry capture timing
- docs: add truthful video failure branch
- docs: add production video package
- docs: align public analysis claims
- docs: prepare read-only judge handoff
- test: isolate viewer wrapper fixture

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

### AGENTS.md

```markdown
# InOrdo engineering guide

## Product purpose

InOrdo helps small teams respond safely when new evidence changes a project. It preserves the source update, uses GPT-5.6 to draft a structured interpretation, deterministically traverses explicit dependencies, and presents recovery actions for selective human approval. Applied internal operations must be attributable and reversible where supported.

The product sequence is: **evidence → impact → proposal → approval → history and undo**.

## P0 scope

- Native project records for tasks, milestones, decisions, events, risks, and artifacts.
- Explicit directed dependencies between records.
- Pasted source updates with immutable raw evidence.
- Server-side GPT-5.6 structured extraction and recovery drafting with Zod validation.
- Deterministic direct and downstream impact traversal with explainable paths.
- Per-action human approval before any mutation.
- Reversible internal operations, operation history, undo, and a deterministic demo reset.
- One isolated, synthetic demo workspace and an accessible responsive web interface.

## Explicit non-goals

- Autonomous model-driven mutations.
- External connectors or sync engines.
- Embeddings, vector search, RAG, or semantic search infrastructure.
- Django, FastAPI, n8n, Firebase, Neon, Railway, Neo4j, or Redis.
- Enterprise administration, native mobile apps, or production-readiness claims during Build Week.

## Architecture boundaries

- Use npm, Node 22, TypeScript, Next.js App Router under `src/`, React Server Components by default, Tailwind CSS, Supabase, and the OpenAI SDK.
- Browser code may use only `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY`. It must never import or reference service-role or OpenAI secrets.
- OpenAI calls run only in server-only modules or server routes/actions. Validate all model output with Zod before it enters domain logic.
- The model may extract a candidate change and draft recovery actions. Deterministic application code owns graph traversal, authorization, approval checks, mutations, operation records, and undo.
- **Model output never directly mutates data.** A validated proposal remains inert until a human explicitly approves a specific action and the server rechecks authorization and current state.
- Supabase migrations, RLS, and server-side authorization are the source of truth for data access. Never rely on UI visibility as authorization.
- Keep the core product standalone. Do not add connector packages or external workflow services.

## Ownership

- **Deston:** Supabase schema and migrations, RLS and authorization, server contracts, OpenAI integration, deterministic impact logic, mutation/undo services, and demo reset safety.
- **Andres:** information architecture, interface copy, visual system, accessibility, responsive behavior, frontend components, and demo presentation.
- Shared ownership: typed boundaries between frontend and server, end-to-end workflow behavior, security review, QA, release readi
[truncated — 1677 more characters]
```

### .github/pull_request_template.md

```markdown
## Summary

- What changed?
- Why is it needed for InOrdo?

## Ownership and boundaries

- Owner: Deston / Andres / Shared
- Contracts changed: none / list them
- Does this touch schema, RLS, authorization, OpenAI, mutation, undo, or environment handling? Explain.

## Verification

- [ ] `npm run lint`
- [ ] `npm run typecheck`
- [ ] `npm run test:run`
- [ ] `npm run build`
- [ ] `git diff --check`
- [ ] Browser or Playwright coverage added when relevant

## Safety and product truth

- [ ] No secret or private data is included.
- [ ] Model output cannot directly mutate data.
- [ ] Human approval and authorization are enforced for mutations.
- [ ] UI and documentation do not claim unfinished behavior works.
- [ ] Synthetic fixture data is visibly labeled.

## Screenshots or evidence

Add before/after screenshots, test output, migration notes, or dependency paths when useful.

```

### package.json

```
{
  "name": "inordo",
  "version": "0.1.0",
  "private": true,
  "engines": {
    "node": "22.x"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "test": "vitest",
    "test:run": "vitest run",
    "test:e2e": "playwright test"
  },
  "dependencies": {
    "@supabase/ssr": "^0.12.3",
    "@supabase/supabase-js": "^2.110.7",
    "clsx": "^2.1.1",
    "date-fns": "^4.4.0",
    "lucide-react": "^1.25.0",
    "next": "16.2.10",
    "openai": "^6.48.0",
    "react": "19.2.4",
    "react-dom": "19.2.4",
    "tailwind-merge": "^3.6.0",
    "zod": "^4.4.3"
  },
  "devDependencies": {
    "@playwright/test": "^1.61.1",
    "@tailwindcss/postcss": "^4",
    "@testing-library/jest-dom": "^6.9.1",
    "@testing-library/react": "^16.3.2",
    "@testing-library/user-event": "^14.6.1",
    "@types/node": "^22.20.1",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.2.10",
    "jsdom": "^29.1.1",
    "supabase": "2.109.1",
    "tailwindcss": "^4",
    "typescript": "^5",
    "vitest": "^4.1.10"
  },
  "overrides": {
    "postcss": "8.5.10"
  }
}

```

### src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";

import { SkipLink } from "@/components/skip-link";

import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "InOrdo · Project change under control",
  description:
    "InOrdo turns project updates into evidence-backed, reviewable changes with deterministic impact paths and human approval.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
      data-scroll-behavior="smooth"
    >
      <body className="min-h-full">
        <SkipLink />
        {children}
      </body>
    </html>
  );
}

```

### src/app/page.tsx

```typescript
import {
  ArrowDown,
  FileText,
  GitBranch,
  LockKeyhole,
  Network,
  ShieldCheck,
  Sparkles,
} from "lucide-react";
import Link from "next/link";

const workflow = [
  {
    title: "Evidence",
    description:
      "Preserve the original update and review the structured change beside it.",
    icon: FileText,
  },
  {
    title: "Deterministic impact",
    description:
      "Traverse explicit dependencies so every downstream item has a visible path.",
    icon: Network,
  },
  {
    title: "Recovery draft",
    description:
      "Use the configured bounded server-side provider to draft recovery actions, never to decide what runs.",
    icon: Sparkles,
  },
  {
    title: "Human approval",
    description:
      "Apply only selected internal changes and keep a reversible operation record.",
    icon: ShieldCheck,
  },
] as const;

export default function Home() {
  return (
    <main id="main-content" tabIndex={-1}>
      <header className="mx-auto flex w-full max-w-[90rem] items-center justify-between px-5 py-6 sm:px-8 lg:px-12">
        <a
          className="inline-flex items-center gap-3 rounded-sm font-semibold tracking-[-0.04em] text-ink focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-signal"
          href="#main-content"
          aria-label="InOrdo home"
        >
          <span
            className="grid size-9 place-items-center border border-ink bg-ink font-mono text-[0.65rem] tracking-[0.08em] text-paper"
            aria-hidden="true"
          >
            IO
          </span>
          <span className="text-lg">InOrdo</span>
        </a>
        <span className="border border-rule bg-white/60 px-3 py-1.5 font-mono text-[0.68rem] uppercase tracking-[0.14em] text-muted">
          Evidence review build
        </span>
      </header>

      <section className="mx-auto grid w-full max-w-[90rem] gap-12 px-5 pb-16 pt-10 sm:px-8 sm:pt-16 lg:grid-cols-[minmax(0,0.88fr)_minmax(32rem,1.12fr)] lg:items-center lg:gap-16 lg:px-12 lg:pb-24 lg:pt-20">
        <div className="max-w-2xl">
          <p className="mb-5 flex items-center gap-3 font-mono text-[0.72rem] uppercase tracking-[0.16em] text-signal">
            <span className="h-px w-8 bg-signal" aria-hidden="true" />
            Work & Productivity · Build Week
          </p>
          <h1 className="max-w-[12ch] text-balance text-[clamp(3.2rem,7.5vw,7.4rem)] font-semibold leading-[0.88] tracking-[-0.075em] text-ink">
            Control project change without the chain reaction.
          </h1>
          <p className="mt-8 max-w-xl text-pretty text-lg leading-8 text-muted sm:text-xl">
            InOrdo turns an unstructured update into evidence-backed changes,
            traces the work they affect, and keeps people in control before
            anything is applied.
          </p>

          <div
            className="mt-10 border-l-2 border-caution bg-white px-5 py-4 shadow-[0_12px_35px_rgba(23,35,31,0.06)]"
            role="status"
          >
            <p className="font-mono text-[0.68rem] uppercase tracking-[0.14em] text-caution">
              Current state
            </p>
            <p className="mt-1 font-semibold text-ink">
              Protected review workspace connected
            </p>
            <p className="mt-1 text-sm leading-6 text-muted" id="demo-status-note">
              The authenticated UI uses the existing analysis, impact, apply,
              history, and undo contracts. Live results remain environment- and
              backend-state dependent; synthetic previews stay labeled.
            </p>
          </div>

          <Link
            className="mt-6 inline-flex min-h-12 items-center gap-2 bg-ink px-5 text-sm font-semibold text-white transition hover:bg-signal focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-signal"
            href="/app"
            aria-describedby="demo-status-note"
          >
            <LockKeyhole className="size-4" aria-hidden="true" />
            Open demo workspace
          </Link>
        </div>

        <div className="ledger-grid relative border border-rule bg-white p-4 shadow-[0_30px_80px_rgba(23,35,31,0.1)] sm:p-7">
          <div className="mb-7 flex items-center justify-between border-b border-rule pb-4">
            <div>
              <p className="font-mono text-[0.65rem] uppercase tracking-[0.16em] text-muted">
                Illustrative trace · not connected
              </p>
              <h2 className="mt-1 text-lg font-semibold tracking-[-0.03em] text-ink">
                Venue date change
              </h2>
            </div>
            <span className="border border-caution/35 bg-caution-soft px-2.5 py-1 font-mono text-[0.62rem] uppercase tracking-[0.1em] text-caution">
              Review only
            </span>
          </div>

          <div className="grid gap-3 sm:grid-cols-[minmax(0,1fr)_3rem_minmax(0,1fr)] sm:items-center">
            <article className="border border-rule bg-paper p-5">
              <div className="flex items-center justify-between gap-4">
                <span className="font-mono text-[0.64rem] uppercase tracking-[0.14em] text-signal">
                  Evidence
                </span>
                <FileText className="size-4 text-signal" aria-hidden="true" />
              </div>
              <p className="mt-6 text-base font-medium leading-7 text-ink">
                “The venue moved the summit from 12 September to 26 September.”
              </p>
              <p className="mt-5 font-mono text-[0.64rem] text-muted">
                Source text preserved
              </p>
            </article>

            <div className="flex justify-center text-signal" aria-hidden="true">
              <ArrowDown className="size-5 sm:-rotate-90" />
            </div>

            <article className="border border-ink bg-ink p-5 text-paper">
              <div className="flex items-center justify-between gap-4">
                <span clas
[truncated — 3817 more characters]
```

### src/lib/supabase/server.ts

```typescript
import "server-only";

import { createServerClient } from "@supabase/ssr";
import type { SupabaseClient } from "@supabase/supabase-js";
import { cookies } from "next/headers";

import { getPublicEnv } from "@/lib/env/public";
import type { Database } from "@/types/database";

declare const requestScopedClientCapability: unique symbol;

/** Cookie-backed, user-scoped client. Its nominal brand prevents privileged substitution. */
export type ServerSupabaseClient = SupabaseClient<Database> & {
  readonly [requestScopedClientCapability]: "request-scoped";
};

type CreateServerSupabaseClientOptions = {
  /** Route Handlers must apply this same Headers object to their response. */
  responseHeaders?: Headers;
};

export async function createServerSupabaseClient(
  options: CreateServerSupabaseClientOptions = {},
): Promise<ServerSupabaseClient> {
  const env = getPublicEnv();
  const cookieStore = await cookies();

  return createServerClient<Database>(
    env.NEXT_PUBLIC_SUPABASE_URL,
    env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    {
      cookies: {
        getAll: () => cookieStore.getAll(),
        setAll(cookiesToSet, responseHeaders) {
          for (const [name, value] of Object.entries(responseHeaders)) {
            options.responseHeaders?.set(name, value);
          }

          try {
            for (const { name, value, options } of cookiesToSet) {
              cookieStore.set(name, value, options);
            }
          } catch {
            // Server Components cannot set response cookies. The request proxy
            // refreshes them; Server Actions can write through this same client.
          }
        },
      },
    },
  ) as ServerSupabaseClient;
}

```

### src/app/app/layout.tsx

```typescript
import Link from "next/link";
import { redirect } from "next/navigation";
import { LogOut } from "lucide-react";

import { logoutAction } from "@/app/login/actions";
import { ProjectNavigation } from "@/app/app/project-navigation";
import { AuthorizationError } from "@/lib/auth/errors";
import { requireUser, type AuthenticatedUser } from "@/lib/auth/guards";
import { createServerSupabaseClient } from "@/lib/supabase/server";

export const dynamic = "force-dynamic";

export default async function ProtectedAppLayout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  let user: AuthenticatedUser | null = null;
  let authenticationRequired = false;

  try {
    const client = await createServerSupabaseClient();
    user = await requireUser(client);
  } catch (error) {
    if (
      error instanceof AuthorizationError &&
      error.code === "unauthenticated"
    ) {
      authenticationRequired = true;
    } else {
      throw error;
    }
  }

  if (authenticationRequired || !user) {
    redirect("/login?next=/app");
  }

  return (
    <div className="min-h-screen bg-paper">
      <header className="border-b border-rule bg-white">
        <div className="mx-auto flex w-full max-w-[90rem] items-center justify-between gap-5 px-5 py-4 sm:px-8 lg:px-12">
          <Link
            className="inline-flex items-center gap-3 rounded-sm font-semibold tracking-[-0.04em] text-ink focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-signal"
            href="/"
          >
            <span
              className="grid size-9 place-items-center bg-ink font-mono text-[0.65rem] tracking-[0.08em] text-paper"
              aria-hidden="true"
            >
              IO
            </span>
            InOrdo
          </Link>

          <div className="flex min-w-0 items-center gap-3 sm:gap-4">
            <span className="inline-flex border border-signal/25 bg-[#eef1ff] px-2 py-2 font-mono text-[0.58rem] uppercase tracking-[0.08em] text-signal sm:px-3 sm:text-[0.62rem] sm:tracking-[0.1em]">
              <span className="sm:hidden">Synthetic</span>
              <span className="hidden sm:inline">Synthetic demo</span>
            </span>
            <p className="hidden max-w-56 truncate text-sm text-muted sm:block">
              Authenticated session
            </p>
            <form action={logoutAction}>
              <button
                className="inline-flex min-h-10 items-center gap-2 border border-rule bg-white px-3 text-sm font-semibold text-ink transition hover:border-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-signal"
                type="submit"
              >
                <LogOut className="size-4" aria-hidden="true" />
                Sign out
              </button>
            </form>
          </div>
        </div>
        <ProjectNavigation />
      </header>
      {children}
    </div>
  );
}

```

### src/app/login/page.tsx

```typescript
import type { Metadata } from "next";
import Link from "next/link";
import { LockKeyhole, ShieldCheck } from "lucide-react";

import { LoginForm } from "@/app/login/login-form";
import { getSafeRedirect } from "@/lib/auth/redirect";

export const metadata: Metadata = {
  title: "Sign in · InOrdo",
  description: "Sign in to the isolated InOrdo demonstration workspace.",
};

type LoginPageProps = {
  searchParams: Promise<{ next?: string | string[] }>;
};

export default async function LoginPage({ searchParams }: LoginPageProps) {
  const query = await searchParams;
  const requestedNext = Array.isArray(query.next) ? query.next[0] : query.next;
  const nextPath = getSafeRedirect(requestedNext);

  return (
    <main
      className="mx-auto grid min-h-[calc(100vh-4rem)] w-full max-w-[90rem] place-items-center px-5 py-12 sm:px-8"
      id="main-content"
      tabIndex={-1}
    >
      <section className="grid w-full max-w-4xl border border-rule bg-white shadow-[0_30px_80px_rgba(23,35,31,0.1)] lg:grid-cols-[0.9fr_1.1fr]">
        <div className="bg-ink p-8 text-paper sm:p-10">
          <div className="grid size-10 place-items-center border border-white/25 font-mono text-xs tracking-[0.08em]">
            IO
          </div>
          <p className="mt-12 font-mono text-[0.68rem] uppercase tracking-[0.16em] text-[#AEBEFF]">
            Isolated demo access
          </p>
          <h1 className="mt-3 text-4xl font-semibold tracking-[-0.055em]">
            Review real seeded project data.
          </h1>
          <p className="mt-5 text-sm leading-7 text-[#C9D0CD]">
            Authentication and row-level security protect this preview. The
            signed-in workspace keeps source evidence, model inference,
            deterministic impact paths, human approval, and reversible
            operations separately attributable.
          </p>
          <div className="mt-10 flex items-start gap-3 border-t border-white/15 pt-5 text-sm text-[#DCE1DF]">
            <ShieldCheck className="mt-0.5 size-4 shrink-0 text-[#AEBEFF]" aria-hidden="true" />
            <p>The demo password is provisioned outside source control.</p>
          </div>
        </div>

        <div className="p-8 sm:p-10">
          <div className="flex items-center justify-between gap-4">
            <div>
              <p className="font-mono text-[0.66rem] uppercase tracking-[0.15em] text-signal">
                Authentication
              </p>
              <h2 className="mt-2 text-2xl font-semibold tracking-[-0.04em] text-ink">
                Sign in
              </h2>
            </div>
            <LockKeyhole className="size-5 text-muted" aria-hidden="true" />
          </div>

          <LoginForm nextPath={nextPath} />

          <p className="mt-7 text-sm leading-6 text-muted">
            Need access? Ask the demo operator to provision an account. No
            public sign-up is enabled.
          </p>
          <Link
            className="mt-5 inline-flex rounded-sm text-sm font-semibold text-signal underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-signal"
            href="/"
          >
            Return to the landing page
          </Link>
        </div>
      </section>
    </main>
  );
}

```

### src/lib/env/server.ts

```typescript
import "server-only";

import { z } from "zod";

import {
  analysisModes,
  type AnalysisProviderPolicy,
  type GatewayModel,
  type RecordingModel,
} from "@/features/analysis/provider-policy";
import {
  EnvironmentConfigurationError,
  exactNonBlankEnvironmentValueSchema,
  publicEnvSchema,
} from "@/lib/env/public";

const openAIModelSchema = z
  .string()
  .trim()
  .min(1)
  .default("gpt-5.6-luna");

export const applicationEnvSchema = publicEnvSchema.extend({
  SUPABASE_SERVICE_ROLE_KEY: exactNonBlankEnvironmentValueSchema,
  DEMO_PROJECT_SLUG: exactNonBlankEnvironmentValueSchema,
  DEMO_RESET_SECRET: exactNonBlankEnvironmentValueSchema,
});

export const serverEnvSchema = applicationEnvSchema.extend({
  OPENAI_API_KEY: exactNonBlankEnvironmentValueSchema,
  OPENAI_MODEL: openAIModelSchema,
});

const privilegedEnvSchema = z.object({
  NEXT_PUBLIC_SUPABASE_URL: publicEnvSchema.shape.NEXT_PUBLIC_SUPABASE_URL,
  SUPABASE_SERVICE_ROLE_KEY: exactNonBlankEnvironmentValueSchema,
});

const demoEnvSchema = z.object({
  DEMO_PROJECT_SLUG: exactNonBlankEnvironmentValueSchema,
});

const demoResetEnvSchema = demoEnvSchema.extend({
  DEMO_RESET_SECRET: exactNonBlankEnvironmentValueSchema,
});

export const openAIEnvSchema = z.object({
  OPENAI_API_KEY: exactNonBlankEnvironmentValueSchema,
  OPENAI_MODEL: openAIModelSchema,
});

const openAIModelEnvSchema = openAIEnvSchema.pick({ OPENAI_MODEL: true });

const recordingModelName: RecordingModel = "gpt-5.6-luna";
const gatewayModelName: GatewayModel = "openai/gpt-oss-20b";
const gatewayBaseURL = "https://ai-gateway.vercel.sh/v1" as const;

const analysisRuntimeValuesSchema = z.object({
  ANALYSIS_MODE: z.string().optional(),
  AI_GATEWAY_API_KEY: z.string().optional(),
  AI_GATEWAY_MODEL: z.string().optional(),
  OPENAI_API_KEY: z.string().optional(),
  OPENAI_MODEL: z.string().optional(),
});

export type AnalysisRuntimeEnv =
  | {
      mode: "disabled";
      policy: AnalysisProviderPolicy;
      credential: null;
    }
  | {
      mode: "recording";
      policy: AnalysisProviderPolicy;
      credential: { apiKey: string; model: RecordingModel } | null;
    }
  | {
      mode: "auto";
      policy: AnalysisProviderPolicy;
      credential: {
        apiKey: string;
        model: GatewayModel;
        baseURL: typeof gatewayBaseURL;
      } | null;
    };

function baseAnalysisPolicy(
  mode: AnalysisProviderPolicy["mode"],
): AnalysisProviderPolicy {
  return {
    mode,
    recordingReady: false,
    gatewayReady: false,
    recordingModelName,
    gatewayModelName,
  };
}

export function parseAnalysisRuntimeEnv(values: unknown): AnalysisRuntimeEnv {
  const parsed = analysisRuntimeValuesSchema.safeParse(values);
  const parsedMode = parsed.success
    ? z.enum(analysisModes).safeParse(parsed.data.ANALYSIS_MODE)
    : null;
  if (!parsedMode?.success || !parsed.success) {
    return {
      mode: "disabled",
      policy: baseAnalysisPolicy("disabled"),
      credential: null,
    };
  }

  const mode = parsedMode.data;
  if (mode === "recording") {
    const apiKey = exactNonBlankEnvironmentValueSchema.safeParse(
      parsed.data.OPENAI_API_KEY,
    );
    const configuredModel = parsed.data.OPENAI_MODEL ?? recordingModelName;
    const ready = apiKey.success && configuredModel === recordingModelName;

    return {
      mode,
      policy: {
        ...baseAnalysisPolicy(mode),
        recordingReady: ready,
      },
      credential: ready
        ? { apiKey: apiKey.data, model: recordingModelName }
        : null,
    };
  }

  if (mode === "auto") {
    const apiKey = exactNonBlankEnvironmentValueSchema.safeParse(
      parsed.data.AI_GATEWAY_API_KEY,
    );
    const ready =
      apiKey.success && parsed.data.AI_GATEWAY_MODEL === gatewayModelName;

    return {
      mode,
      policy: {
        ...baseAnalysisPolicy(mode),
        gatewayReady: ready,
      },
      credential: ready
        ? {
            apiKey: apiKey.data,
            model: gatewayModelName,
            baseURL: gatewayBaseURL,
          }
        : null,
    };
  }

  return {
    mode: "disabled",
    policy: baseAnalysisPolicy("disabled"),
    credential: null,
  };
}

export function getAnalysisRuntimeEnv(): AnalysisRuntimeEnv {
  return parseAnalysisRuntimeEnv({
    ANALYSIS_MODE: process.env.ANALYSIS_MODE,
    AI_GATEWAY_API_KEY: process.env.AI_GATEWAY_API_KEY,
    AI_GATEWAY_MODEL: process.env.AI_GATEWAY_MODEL,
    OPENAI_API_KEY: process.env.OPENAI_API_KEY,
    OPENAI_MODEL: process.env.OPENAI_MODEL,
  });
}

function parseEnvironment<T extends z.ZodType>(
  scope: string,
  schema: T,
  values: unknown,
): z.infer<T> {
  const result = schema.safeParse(values);
  if (!result.success) {
    throw new EnvironmentConfigurationError(scope, result.error.issues);
  }
  return result.data;
}

export function getServerEnv(): z.infer<typeof serverEnvSchema> {
  return parseEnvironment("Server", serverEnvSchema, {
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    NEXT_PUBLIC_SUPABASE_ANON_KEY:
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
    OPENAI_API_KEY: process.env.OPENAI_API_KEY,
    OPENAI_MODEL: process.env.OPENAI_MODEL,
    DEMO_PROJECT_SLUG: process.env.DEMO_PROJECT_SLUG,
    DEMO_RESET_SECRET: process.env.DEMO_RESET_SECRET,
  });
}

export function getPrivilegedSupabaseEnv() {
  return parseEnvironment("Privileged Supabase", privilegedEnvSchema, {
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
  });
}

export function getDemoProjectSlug(): string {
  return parseEnvironment("Demo project", demoEnvSchema, {
    DEMO_PROJECT_SLUG: process.env.DEMO_PROJECT_SLUG,
  }).DEMO_PROJECT_SLUG;
}

export function parseDemoResetEnv(values: unknown) {
  return parseEnvironment("Demo reset", demoResetEnvSchema, values);
}

export function getDemoResetEnv(): z.infer<typeof de
[truncated — 648 more characters]
```

### src/app/app/page.tsx

```typescript
import { format, parseISO } from "date-fns";
import {
  ArrowRight,
  CalendarDays,
  FileText,
  GitBranch,
  ListChecks,
  ShieldAlert,
  Users,
} from "lucide-react";
import Link from "next/link";
import { notFound, redirect } from "next/navigation";

import { buildGuidedDemoTargets } from "@/app/app/demo-targets";
import { GuidedDemoCallout } from "@/app/app/guided-demo-callout";
import { ImpactWorkflow } from "@/app/app/impact-workflow";
import { buildWorkflowOverview } from "@/app/app/workflow-overview";
import { buildAnalysisAvailability } from "@/features/analysis/provider-policy";
import { createProjectRecordOperations } from "@/features/project-records/operations";
import { AuthorizationError } from "@/lib/auth/errors";
import { requireProjectToWorkspace, requireUser } from "@/lib/auth/guards";
import type { WorkspaceRole } from "@/lib/auth/membership";
import { getAnalysisRuntimeEnv } from "@/lib/env/server";
import {
  getDemoWorkspaceProject,
  getProjectOverview,
  listProjectItems,
} from "@/lib/repositories/project-data";
import { getImpactWorkflowData } from "@/lib/repositories/impact-review";
import { createServerSupabaseClient } from "@/lib/supabase/server";

export const dynamic = "force-dynamic";

type DemoWorkspacePageProps = {
  searchParams: Promise<Record<string, string | string[] | undefined>>;
};

const uuidPattern =
  /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

function formatDate(value: string | null) {
  return value ? format(parseISO(value), "MMM d, yyyy") : "Not scheduled";
}

function words(value: string) {
  return value.replaceAll("_", " ");
}

export default async function DemoWorkspacePage({
  searchParams,
}: DemoWorkspacePageProps) {
  const analysisAvailability = buildAnalysisAvailability(
    getAnalysisRuntimeEnv().policy,
  );
  const query = await searchParams;
  const requestedAnalysisId =
    typeof query.analysisRequestId === "string" &&
    uuidPattern.test(query.analysisRequestId)
      ? query.analysisRequestId
      : undefined;
  let loadError: unknown;
  let result:
    | {
        overview: Awaited<ReturnType<typeof getProjectOverview>>;
        items: Awaited<ReturnType<typeof listProjectItems>>;
        workflow: Awaited<ReturnType<typeof getImpactWorkflowData>>;
        dependencies: Awaited<
          ReturnType<
            ReturnType<typeof createProjectRecordOperations>["listDependencies"]
          >
        >;
        role: WorkspaceRole;
      }
    | undefined;

  try {
    const client = await createServerSupabaseClient();
    const user = await requireUser(client);
    const demoProject = await getDemoWorkspaceProject(client);
    const scope = await requireProjectToWorkspace(
      client,
      user.id,
      demoProject.workspace_id,
      demoProject.id,
    );
    const projectRecords = createProjectRecordOperations({ client });
    const [overview, items, workflow, dependencies] =
      await Promise.all([
        getProjectOverview(client, scope),
        listProjectItems(client, scope),
        getImpactWorkflowData(client, scope, {
          analysisRequestId: requestedAnalysisId,
        }),
        projectRecords.listDependencies(scope.projectId),
      ]);

    result = {
      overview,
      items,
      workflow,
      dependencies,
      role: scope.membership.role,
    };
  } catch (error) {
    loadError = error;
  }

  if (loadError instanceof AuthorizationError) {
    if (loadError.code === "unauthenticated") redirect("/login?next=/app");
    if (loadError.code === "not_found") notFound();
  }
  if (loadError) throw loadError;
  if (!result) throw new Error("The project overview could not be loaded.");

  const {
    dependencies,
    items,
    overview,
    role,
    workflow,
  } = result;
  const workspace = overview.project.workspace;
  const eventItem = items.data.find((item) => item.item_type === "event");
  const upcomingMilestones = items.data
    .filter(
      (item) =>
        item.item_type === "milestone" &&
        !["completed", "cancelled"].includes(item.status),
    )
    .sort((left, right) =>
      (left.due_date ?? "9999-12-31").localeCompare(
        right.due_date ?? "9999-12-31",
      ),
    )
    .slice(0, 3);
  const openRisksAndBlockers = items.data.filter(
    (item) =>
      (item.item_type === "risk" || item.status === "blocked") &&
      !["completed", "cancelled"].includes(item.status),
  );
  const team = [
    ...new Map(
      items.data.flatMap((item) =>
        item.owner
          ? [[item.owner.id, item.owner] as const]
          : [],
      ),
    ).values(),
  ].sort((left, right) => left.display_name.localeCompare(right.display_name));
  const latestSource = workflow.analysis?.source;
  const workflowOverview = buildWorkflowOverview({
    analysisLoadFailed: workflow.analysisLoadFailed,
    operationsLoadFailed: workflow.operationsLoadFailed,
    impactCount: workflow.analysis?.impacts.length ?? 0,
    hasProposal: Boolean(workflow.analysis?.proposal),
    operationCount: workflow.operations.length,
  });
  const summaryCards = [
    { label: "Project items", value: overview.counts.items, icon: ListChecks },
    { label: "Dependencies", value: dependencies.length, icon: GitBranch },
    {
      label: "Risks / blockers",
      value: openRisksAndBlockers.length,
      icon: ShieldAlert,
    },
    { label: "Evidence updates", value: overview.counts.sources, icon: FileText },
  ] as const;

  const quickLinks = [
    {
      href: "/app/items",
      label: "Items",
      detail: `${items.total} canonical records`,
    },
    {
      href: "/app/dependencies",
      label: "Dependencies",
      detail: `${dependencies.length} explicit relationships`,
    },
    {
      href: "#impact-status",
      label: "Impacts",
      detail: workflowOverview.impactDetail,
    },
    {
      href: "#history-status",
      label: "History",
      detail: workflowOverview.historyDetail,
    },
  ] as const;

  return (
    <main
      classN
[truncated — 12421 more characters]
```

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