# Project export: Golden Gate

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: TreeHacks 2026
- Tagline: Preserve institutional knowledge in workforce transition
- Devpost: https://devpost.com/software/golden-gate-l3f5xn
- GitHub: https://github.com/ericaezhou/golden_gate
- Video: https://www.youtube.com/embed/gqkdvaKBa_o?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — shenjm (21 commits), Sirui Li (13 commits), Magnus Sesodia (12 commits), ericaez (4 commits)

## Devpost submission (written by the team)

### Inspiration

Many organizations face the same structural challenge: tribal and tacit knowledge are critical assets, yet they remain hidden, fragmented, and difficult to reuse. Key decisions are often shaped by unwritten assumptions, and dependencies are understood informally rather than explicitly documented. During workforce transitions – employee turnover, project handoffs, or re-org – that contextual layer is often lost. As a result, teams spend lots of time reconstructing context instead of moving forward. Golden Gate was built to address this discontinuity. We systematically preserve and transfer institutional intelligence. When people move, organizational context should remain accessible.

### What it does

Golden Gate is an agentic knowledge preservation and transfer platform. The system analyzes project artifacts in iterative passes to surface implicit assumptions, decision logic, and potential knowledge gaps. It then conducts structured, context-aware interviews with the departing employee to clarify reasoning, resolve inconsistencies, and formalize tacit understanding. The output is a structured onboarding package designed to accelerate continuity. This includes: A synthesized summary of project artifacts A structured summary of interview insights A knowledge graph to visualize relationships and dependencies An interactive Q&A agent trained on the preserved knowledge The goal is to make institutional context accessible to the incoming team member, reducing time spent reconstructing prior thinking and enabling a smoother transition. Core Flow Intelligent File Analysis Upload project artifacts (Excel, Python, SQL, Jupyter, PDFs, PowerPoints, etc.) Multi-pass deep-dive analysis extracts: Documented logic Hidden dependencies Undocumented assumptions Knowledge gaps Intelligent File Analysis Upload project artifacts (Excel, Python, SQL, Jupyter, PDFs, PowerPoints, etc.) Multi-pass deep-dive analysis extracts: Documented logic Hidden dependencies Undocumented assumptions Knowledge gaps Documented logic Hidden dependencies Undocumented assumptions Knowledge gaps Gap Detection & Smart Question Generation AI identifies inconsistencies and missing context Prioritizes questions by risk and impact References specific files, formulas, and code Gap Detection & Smart Question Generation AI identifies inconsistencies and missing context Prioritizes questions by risk and impact References specific files, formulas, and code Context-Aware Conversational Interview Conducts natural, structured interviews Dynamically discovers new gaps in real time Maintains cross-file awareness Extracts structured facts with confidence scoring Conducts natural, structured interviews Dynamically discovers new gaps in real time Maintains cross-file awareness Extracts structured facts with confidence scoring Onboarding & Transfer Package Generation Synthesizes knowledge into organized documentation: Decisions Rules Dependencies Risks Historical reasoning Enhances original project files with extracted insights Trains a living AI assistant on the full knowledge base Onboarding & Transfer Package Generation Synthesizes knowledge into organized documentation: Decisions Rules Dependencies Risks Historical reasoning Decisions Rules Dependencies Risks Historical reasoning Enhances original project files with extracted insights Trains a living AI assistant on the full knowledge base Living Knowledge Agent New hires and teammates interact with an AI trained on the preserved knowledge Provides source citations and confidence levels Keeps knowledge searchable and durable Living Knowledge Agent New hires and teammates interact with an AI trained on the preserved knowledge Provides source citations and confidence levels Keeps knowledge searchable and durable Golden Gate doesn’t just store documents — it preserves intelligence.

### How we built it

Backend LangGraph for multi-step agentic workflows with human-in-the-loop interrupt/resume Python + FastAPI for API routing and streaming OpenAI GPT-5.2 powering: Deep-dive analysis Question generation Conversational interviewing Knowledge synthesis Deep-dive analysis Question generation Conversational interviewing Knowledge synthesis Custom file parsers (10+) for: Excel formulas Python AST SQL schemas Jupyter notebooks Structured PDFs and presentations Excel formulas Python AST SQL schemas Jupyter notebooks Structured PDFs and presentations Frontend Next.js 14 + TypeScript Tailwind CSS Server-Sent Events (SSE) for real-time pipeline visualization: Parse → Deep Dive → Gap Detection → Questions → Interview → Synthesis Technical Innovation Multi-pass analysis per file (structure → critique → tacit extraction) Cross-file reasoning Dynamic question backlog generation Structured LLM-synthesized summaries instead of raw transcripts Real-time AI progress streaming

### Challenges we ran into

Maintaining conversational quality across 50K+ token project contexts Managing LangGraph state reducers during interrupt/resume cycles Streaming real-time AI events to the frontend without memory leaks Deduplicating knowledge gaps across multiple artifacts without losing nuance Designing prompts that extract tacit reasoning rather than generic explanations

### Accomplishments we're proud of

Built a production-ready, multi-agent workflow system in 36 hours Implemented 10+ working file parsers Created a fully context-aware conversational interview engine Achieved live, real-time AI progress visualization Zero mock backend flows — fully functional pipeline Designed specifically for conversational excellence in the Decagon track Established strong product branding around knowledge continuity 🌉

### What we learned

Most critical knowledge lives in the gaps between files Context depth dramatically improves conversational intelligence Agentic workflows outperform linear prompt chains for complex reasoning Real-time streaming significantly improves trust and UX Structured synthesis is more valuable than raw transcripts Knowledge preservation is a universal problem — not just an HR problem

### What's next

Enhanced AI Capabilities Voice/video knowledge capture Multi-employee knowledge synthesis Automated knowledge graph generation Enterprise Integrations Slack and Teams triggers for transition events HRIS integrations (Workday, BambooHR) Compliance tracking (SOX, GDPR) Multi-tenant SaaS with role-based access control Advanced Onboarding Adaptive onboarding paths by role Knowledge verification quizzes Suggested peer connections Continuous Knowledge Management Living documentation that updates over time Proactive gap detection before transitions occur Knowledge health scores for teams and projects Golden Gate’s vision is to make knowledge preservation automatic. Preserve knowledge. Power every transition. 🌉

## README (from the GitHub repository)

# golden_gate

## Detected evidence (automated analysis)

Indexed codebase: 127 recognized source files, 782 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository
- AI coding agent: Cursor — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 154)

```
.claude/claude.md
.cursor/claude.mdc
.cursor/coding_style.mdc
.env.example
.gitignore
backend/__init__.py
backend/config.py
backend/graphs/__init__.py
backend/graphs/offboarding_graph.py
backend/graphs/onboarding_graph.py
backend/graphs/subgraphs/__init__.py
backend/graphs/subgraphs/file_deep_dive.py
backend/main.py
backend/models/__init__.py
backend/models/artifacts.py
backend/models/questions.py
backend/models/state.py
backend/nodes/__init__.py
backend/nodes/build_qa_context.py
backend/nodes/concatenate.py
backend/nodes/deep_dive.py
backend/nodes/generate_package.py
backend/nodes/global_summarize.py
backend/nodes/interview.py
backend/nodes/parse_files.py
backend/nodes/reconcile_questions.py
backend/parse_cli.py
backend/parsers/__init__.py
backend/parsers/cross_references.py
backend/parsers/docx_parser.py
backend/parsers/excel_parser.py
backend/parsers/notebook_parser.py
backend/parsers/pdf_parser.py
backend/parsers/pptx_parser.py
backend/parsers/python_parser.py
backend/parsers/sql_parser.py
backend/parsers/sqlite_parser.py
backend/parsers/text_parser.py
backend/routes/__init__.py
backend/routes/interview.py
backend/routes/offboarding.py
backend/routes/onboarding.py
backend/routes/session.py
backend/services/__init__.py
backend/services/llm.py
backend/services/storage.py
backend/test_parsers.py
backend/tests/__init__.py
backend/tests/test_deep_dive.py
backend/tests/test_framework.py
backend/tests/test_generate_package.py
backend/tests/test_integration.py
backend/tests/test_reconcile_questions.py
data_delivery/.gitignore
data_delivery/create_graph.py
data_delivery/kg.py
data_delivery/neo4j_.py
data_delivery/run.py
data/archive/forecast_and_valuation_model.ipynb
data/archive/policy_compliant_queries.sql
data/archive/portfolio_risk.db
data/loss_forecast_model.py
data/portfolio_risk.db
data/risk_queries.sql
data/run_notes.txt
data/sessions/.gitkeep
data/stress_testing.ipynb
docs/1_parsing.md
docs/2_deep_dive_analysis.md
docs/3_question_generation.md
docs/4_interview_loop.md
docs/5_interview_summary.md
docs/general_design.md
docs/how_to_run.md
docs/implementation_design.md
docs/KG.md
docs/node_interfaces.md
docs/onboarding_qa_loop.md
docs/progress.md
next.config.js
output/parsed/_cross_references.json
output/parsed/alice_past_runs_and_sensitivity.json
output/parsed/Board_Risk_Committee_Deck.json
output/parsed/forecast_and_valuation_model.json
output/parsed/policy_compliant_queries.json
output/parsed/portfolio_risk.json
output/parsed/Q3_board_risk_memo_draft.json
output/parsed/risk_policy_v3_2.json
output/parsed/sessions.json
package.json
postcss.config.js
public/.gitkeep
public/artifacts/alice-chen/Escalation_Policy.md
public/artifacts/alice-chen/loss_model.py
public/artifacts/alice-chen/Q3_Loss_Forecast.json
public/artifacts/alice-chen/Risk_Committee_Notes.md
public/artifacts/alice-chen/Segment_Analysis.json
public/artifacts/alice-chen/threshold_config.py
pyproject.toml
README.md
scripts/generate_demo_data.py
src/app/api/analyze/route.ts
src/app/api/chat/route.ts
src/app/api/generate-agent/route.ts
src/app/api/scan/route.ts
src/app/components/FileIcon.tsx
src/app/globals.css
src/app/graph/page.tsx
src/app/layout.tsx
src/app/offboarding/components/AnalysisStep.tsx
src/app/offboarding/components/CompleteStep.tsx
src/app/offboarding/components/EmployeeStep.tsx
src/app/offboarding/components/HandoffStep.tsx
src/app/offboarding/components/UploadStep.tsx
src/app/offboarding/page.tsx
src/app/onboarding/components/OnboardingGraph.tsx
src/app/onboarding/components/OnboardingSummary.tsx
src/app/onboarding/components/QAChat.tsx
src/app/onboarding/page.tsx
src/app/page.tsx
[34 more files omitted for size]
```

### Dependencies

- package.json: @anthropic-ai/sdk@^0.71.2, @types/dagre@^0.7.53, @types/node@^20.0.0, @types/react@^18.2.0, @types/react-dom@^18.2.0, autoprefixer@^10.4.0, dagre@^0.8.5, eslint@^8.0.0, eslint-config-next@^14.2.0, next@^14.2.0, postcss@^8.4.0, react@^18.2.0, react-dom@^18.2.0, reactflow@^11.11.4, tailwindcss@^3.4.0, typescript@^5.0.0
- pyproject.toml: chromadb@>=0.6, fastapi@>=0.115, httpx@>=0.28, langchain-openai@>=0.3, langgraph@>=0.4, nbformat@>=5.9.0, openai@>=1.60, openpyxl@>=3.1.0, pydantic@>=2.10, pydantic-settings@>=2.7, pymupdf4llm@>=0.0.5, pytest@>=8.0, pytest-asyncio@>=0.25, python-docx@>=1.0.0, python-dotenv@>=1.0, python-multipart@>=0.0.18, python-pptx@>=0.6.21, sqlglot@>=20.0.0, sse-starlette@>=2.2, uvicorn[standard]@>=0.34

### Recent commits (newest first)

- Modify front-end color
- change frontend for KG
- Merge branch 'magnus3'
- onboarding needs to be connected
- integrating evertything
- integrating evertything
- Merge pull request #5 from ericaezhou/on_boarding_agent
- Merge remote-tracking branch 'origin' into on_board_agent
- fix bugs in qa_loop
- Merge branch 'magnus2'
- UI changes
- Merge pull request #4 from ericaezhou/on_boarding_agent
- Merge remote-tracking branch 'origin/main' into on_board_agent
- Add documentation for on-boarding
- added onboard abs
- update interview loop and step 3-5 documentations
- Add documentation for KG
- Merge branch 'magnus'
- cleaning up steps 1 and 2
- Merge pull request #3 from ericaezhou/on_boarding_agent

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

### tasks/todo.md

```markdown
# Stage 1: File Parsing System

## Implementation

- [x] `__init__.py` — ParseResult + registry + dispatcher
- [x] `requirements.txt` — dependencies
- [x] `text_parser.py` — simplest parser
- [x] `excel_parser.py` — most complex, most important
- [x] `docx_parser.py` — Word documents
- [x] `python_parser.py` — AST-based code analysis
- [x] `sql_parser.py` — sqlglot-based
- [x] `pdf_parser.py` — pymupdf4llm wrapper
- [x] `notebook_parser.py` — nbformat + ast
- [x] `pptx_parser.py` — python-pptx
- [x] `cross_references.py` — post-processing inventory matching
- [x] `parse_cli.py` — CLI test harness

## Verification

- [x] `pip install -r requirements.txt` succeeds
- [x] `python parse_cli.py ../data/` — all 7 demo files parse without errors (0 warnings)
- [x] `python parse_cli.py ../data/ --summary` — each file shows non-zero content lines
- [x] `python parse_cli.py ../data/ --cross-refs` — works (no cross-refs in demo data because files reference each other semantically, not by filename — that's by design for Stage 2 LLM analysis)
- [x] Spot-check: Excel content human-readable with all data visible in markdown tables

## Notes

- Fixed Python 3.9 compatibility (`str | None` -> `from __future__ import annotations`)
- Fixed `re.findall` bug in pdf/pptx parsers (capturing group returns extension only, not full match)
- Fixed openpyxl named ranges API (`definedName` -> `values()`)
- Demo data has no formulas (pure data) and no explicit cross-file references — both features work correctly when present

```

### docs/onboarding_qa_loop.md

```markdown
# QA loop (onboarding graph)

The **`qa_loop`** node is the interactive Q&A step in the onboarding LangGraph. A new hire asks questions about the project; the agent answers using only the persisted onboarding artifacts (no vector DB).

## Behavior

1. **Pause for input** — The node calls `interrupt()` so the graph pauses and the frontend can send the user’s question.
2. **Load context** — From the session store it loads:
   - Interview summary (`interview_summary.txt`)
   - Global text summary (`text_summary.txt`)
   - Knowledge graph (`knowledge_graph.json`)
   - Deep dives (`deep_dives.txt`)
3. **Answer** — `QA_SYSTEM_PROMPT` is filled with that context and the user question. The LLM is called with this as the system prompt and `state["chat_history"]` as messages. Responses must cite artifacts (e.g. `[Deep Dive: file.py]`, `[KG]`) and say when the artifacts don’t contain the answer.
4. **Update state** — The new user message and assistant reply are appended to `chat_history`; `current_mode` is set to `"qa"`.
5. **Loop** — The graph has an edge from `qa_loop` back to `qa_loop`, so after each answer it waits again for the next question.

## State

- **Reads:** `session_id`, `chat_history`, session storage (artifacts above).
- **Writes:** `chat_history`, `current_mode` (`"qa"`).

## Interrupt payload

When the graph is resumed after `interrupt()`, the frontend must send the value that was passed to `interrupt()` (e.g. the user’s question string). That value is used as `user_input` in the prompt.

## Prompt and citations

The agent is instructed to cite one of: `[Interview Summary]`, `[Text Summary]`, `[KG]`, `[Deep Dive: <file or section>]` for each claim, and to say “I don’t have specific information on …” when the artifacts don’t cover the topic. See `QA_SYSTEM_PROMPT` in `onboarding_graph.py` for the full rules.

```

### package.json

```
{
  "name": "bridge-ai",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.71.2",
    "dagre": "^0.8.5",
    "next": "^14.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "reactflow": "^11.11.4"
  },
  "devDependencies": {
    "@types/dagre": "^0.7.53",
    "@types/node": "^20.0.0",
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.0",
    "autoprefixer": "^10.4.0",
    "eslint": "^8.0.0",
    "eslint-config-next": "^14.2.0",
    "postcss": "^8.4.0",
    "tailwindcss": "^3.4.0",
    "typescript": "^5.0.0"
  }
}

```

### pyproject.toml

```
[project]
name = "golden-gate"
version = "0.1.0"
description = "Offboarding → Onboarding knowledge capture agent"
requires-python = ">=3.11"

dependencies = [
    # --- LLM & Orchestration ---
    "langgraph>=0.4",
    "langchain-openai>=0.3",
    "openai>=1.60",

    # --- Backend API ---
    "fastapi>=0.115",
    "uvicorn[standard]>=0.34",
    "python-multipart>=0.0.18",
    "sse-starlette>=2.2",

    # --- File Parsers (existing) ---
    "openpyxl>=3.1.0",
    "python-docx>=1.0.0",
    "nbformat>=5.9.0",
    "sqlglot>=20.0.0",
    "pymupdf4llm>=0.0.5",
    "python-pptx>=0.6.21",

    # --- Vector Store & Embeddings ---
    "chromadb>=0.6",

    # --- Utilities ---
    "pydantic>=2.10",
    "pydantic-settings>=2.7",
    "python-dotenv>=1.0",

    # --- Testing ---
    "pytest>=8.0",
    "pytest-asyncio>=0.25",
    "httpx>=0.28",
]

[project.scripts]
serve = "backend.main:start"

[tool.pytest.ini_options]
testpaths = ["backend/tests"]
asyncio_mode = "auto"

[tool.ruff]
line-length = 88
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "W"]

```

### backend/main.py

```python
"""FastAPI application entry point.

Run with:
    uv run uvicorn backend.main:app --reload --reload-dir backend --port 8000

Or via the project script:
    uv run serve
"""

from __future__ import annotations

import logging
import sys

import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from backend.config import settings
from backend.routes import interview, offboarding, onboarding, session

# ------------------------------------------------------------------
# Logging
# ------------------------------------------------------------------
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s  %(levelname)-8s  %(name)s  %(message)s",
)
logger = logging.getLogger(__name__)

# ------------------------------------------------------------------
# API key validation
# ------------------------------------------------------------------
if not settings.OPENAI_API_KEY:
    logger.warning(
        "OPENAI_API_KEY is not set. LLM calls will fail. "
        "Set it in .env or as an environment variable."
    )

# ------------------------------------------------------------------
# App
# ------------------------------------------------------------------
app = FastAPI(
    title="Golden Gate — Knowledge Transfer Agent",
    version="0.1.0",
    description="Offboarding → Onboarding knowledge capture pipeline",
)

# CORS — allow the Next.js frontend
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ------------------------------------------------------------------
# Routers
# ------------------------------------------------------------------
app.include_router(offboarding.router)
app.include_router(interview.router)
app.include_router(onboarding.router)
app.include_router(session.router)


# ------------------------------------------------------------------
# Health check
# ------------------------------------------------------------------
@app.get("/api/health")
async def health():
    return {"status": "ok", "version": "0.1.0"}


# ------------------------------------------------------------------
# CLI entry point
# ------------------------------------------------------------------
def start():
    """Entry point for `uv run serve`."""
    uvicorn.run(
        "backend.main:app",
        host=settings.HOST,
        port=settings.PORT,
        reload=True,
        reload_dirs=["backend"],
    )


if __name__ == "__main__":
    start()

```

### src/app/layout.tsx

```typescript
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Golden Gate - Offboarding & Onboarding Agent',
  description: 'AI-powered offboarding and onboarding knowledge transfer',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={`${inter.className} bg-gg-bg text-gg-text`}>{children}</body>
    </html>
  )
}

```

### src/app/page.tsx

```typescript
'use client'

import Link from 'next/link'
import { useState, useEffect } from 'react'
import { getOffboardedEmployees, OffboardedEmployee } from '@/lib/offboarding-registry'

export default function Home() {
  const [employees, setEmployees] = useState<OffboardedEmployee[]>([])

  useEffect(() => {
    setEmployees(getOffboardedEmployees())
  }, [])

  const hasOffboarded = employees.length > 0

  return (
    <main className="min-h-screen flex flex-col items-center justify-center p-8">
      {/* Hero */}
      <div className="text-center mb-12 max-w-2xl">
        <h1 className="text-5xl font-bold mb-4 text-gg-accent">
          Golden Gate
        </h1>
        <p className="text-gg-secondary text-lg leading-relaxed">
          AI-powered knowledge transfer for seamless employee transitions
        </p>
      </div>

      {/* Action Buttons */}
      <div className="flex gap-6 mb-16">
        <Link
          href="/offboarding"
          className="group flex items-center gap-3 px-8 py-4 bg-gg-card border border-gg-border rounded-gg
                     hover:border-gg-rust/50 hover:shadow-lg transition-all duration-200"
        >
          <span className="w-3 h-3 rounded-full bg-gg-rust group-hover:shadow-[0_0_12px_rgba(192,54,44,0.5)] transition-shadow" />
          <span className="font-semibold text-gg-text">Offboard</span>
        </Link>

        {hasOffboarded ? (
          <button
            disabled
            className="group flex items-center gap-3 px-8 py-4 bg-gg-card border border-gg-border rounded-gg
                       opacity-80 cursor-default"
            title="Select an employee from the table below to onboard"
          >
            <span className="w-3 h-3 rounded-full bg-gg-gold" />
            <span className="font-semibold text-gg-text">Onboard</span>
          </button>
        ) : (
          <div
            className="flex items-center gap-3 px-8 py-4 bg-gg-card border border-gg-border rounded-gg
                       opacity-40 cursor-not-allowed"
            title="Complete an offboarding first"
          >
            <span className="w-3 h-3 rounded-full bg-gg-gold/50" />
            <span className="font-semibold text-gg-muted">Onboard</span>
          </div>
        )}
      </div>

      {/* Recently Offboarded Table */}
      <div className="w-full max-w-3xl">
        <div className="bg-gg-card border border-gg-border rounded-gg overflow-hidden shadow-gg-glow">
          <div className="px-6 py-4 border-b border-gg-border">
            <h2 className="text-sm font-semibold text-gg-secondary uppercase tracking-wider">
              Recently Offboarded
            </h2>
          </div>

          {employees.length === 0 ? (
            <div className="px-6 py-12 text-center">
              <p className="text-gg-muted text-sm">No offboarded employees yet.</p>
              <p className="text-gg-muted text-xs mt-1">Start an offboarding to capture knowledge.</p>
            </div>
          ) : (
            <table className="w-full">
              <thead>
                <tr className="border-b border-gg-border text-xs text-gg-muted uppercase tracking-wider">
                  <th className="px-6 py-3 text-left font-medium">Name</th>
                  <th className="px-6 py-3 text-left font-medium">Role</th>
                  <th className="px-6 py-3 text-left font-medium">Project</th>
                  <th className="px-6 py-3 text-left font-medium">Date</th>
                  <th className="px-6 py-3 text-right font-medium">Action</th>
                </tr>
              </thead>
              <tbody>
                {employees.map((emp) => (
                  <tr key={emp.sessionId} className="border-b border-gg-border/50 last:border-b-0 hover:bg-gg-surface/50 transition-colors">
                    <td className="px-6 py-4 text-sm text-gg-text font-medium">{emp.employeeName}</td>
                    <td className="px-6 py-4 text-sm text-gg-secondary">{emp.roleTitle}</td>
                    <td className="px-6 py-4 text-sm text-gg-secondary">{emp.projectName}</td>
                    <td className="px-6 py-4 text-sm text-gg-muted">
                      {new Date(emp.completedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
                    </td>
                    <td className="px-6 py-4 text-right">
                      <Link
                        href={`/onboarding?session=${emp.sessionId}`}
                        className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium
                                   bg-gg-accent/10 text-gg-accent-light border border-gg-accent/30 rounded-lg
                                   hover:bg-gg-accent/20 transition-colors"
                      >
                        Onboard
                      </Link>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>

      <p className="mt-12 text-xs text-gg-muted">
        Golden Gate &mdash; AI-powered knowledge transfer
      </p>
    </main>
  )
}

```

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

```typescript
import GraphView from "@/components/graph/GraphView";

export default function Page() {
  return (
    <main style={{ height: "100vh" }}>
      <GraphView />
    </main>
  );
}
```

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

```typescript
'use client'

import { Suspense } from 'react'
import { useSearchParams } from 'next/navigation'
import { useState, useEffect } from 'react'
import { getOffboardedEmployee, OffboardedEmployee } from '@/lib/offboarding-registry'
import { OnboardingGraph } from './components/OnboardingGraph'
import { OnboardingSummary } from './components/OnboardingSummary'
import { QAChat } from './components/QAChat'

function OnboardingContent() {
  const searchParams = useSearchParams()
  const sessionId = searchParams.get('session')
  const [employee, setEmployee] = useState<OffboardedEmployee | null>(null)

  useEffect(() => {
    if (sessionId) {
      const emp = getOffboardedEmployee(sessionId)
      setEmployee(emp || null)
    }
  }, [sessionId])

  if (!sessionId) {
    return (
      <main className="min-h-screen flex items-center justify-center">
        <div className="text-center">
          <p className="text-gg-secondary mb-4">No session specified.</p>
          <a href="/" className="text-gg-accent hover:text-gg-accent-light underline">
            Return home
          </a>
        </div>
      </main>
    )
  }

  return (
    <div className="h-screen flex flex-col bg-gg-bg">
      {/* Header */}
      <header className="bg-gg-surface border-b border-gg-border px-6 py-4 flex-shrink-0">
        <div className="flex items-center justify-between">
          <div>
            <h1 className="text-lg font-semibold text-gg-text">
              Onboarding: {employee?.roleTitle || 'Loading...'}
            </h1>
            <p className="text-sm text-gg-secondary">
              {employee?.employeeName ? `Knowledge from ${employee.employeeName}` : 'Loading...'}
              {employee?.projectName ? ` — ${employee.projectName}` : ''}
            </p>
          </div>
          <a
            href="/"
            className="px-4 py-2 text-sm text-gg-secondary hover:text-gg-text border border-gg-border rounded-lg
                       hover:bg-gg-card transition-colors"
          >
            &larr; Back to Home
          </a>
        </div>
      </header>

      {/* Main Content: Graph (60%) | Summary + QA (40%) */}
      <div className="flex-1 flex overflow-hidden">
        {/* Left: Graph */}
        <div className="flex-[3] border-r border-gg-border overflow-hidden">
          <OnboardingGraph sessionId={sessionId} />
        </div>

        {/* Right: Summary + QA stacked */}
        <div className="flex-[2] flex flex-col overflow-hidden">
          {/* Summary */}
          <div className="flex-1 border-b border-gg-border overflow-y-auto">
            <OnboardingSummary sessionId={sessionId} />
          </div>

          {/* QA Chat */}
          <div className="flex-1 flex flex-col overflow-hidden">
            <QAChat sessionId={sessionId} />
          </div>
        </div>
      </div>
    </div>
  )
}

export default function OnboardingPage() {
  return (
    <Suspense fallback={
      <main className="min-h-screen flex items-center justify-center">
        <p className="text-gg-secondary">Loading...</p>
      </main>
    }>
      <OnboardingContent />
    </Suspense>
  )
}

```

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

```typescript
'use client'

import { OffboardingProvider, useOffboarding, OffboardingStep } from '@/context/OffboardingContext'
import { UploadStep } from './components/UploadStep'
import { AnalysisStep } from './components/AnalysisStep'
import { EmployeeStep } from './components/EmployeeStep'
import { HandoffStep } from './components/HandoffStep'
import { CompleteStep } from './components/CompleteStep'

const STEPS: { id: OffboardingStep; label: string }[] = [
  { id: 1, label: 'Upload' },
  { id: 2, label: 'Analysis' },
  { id: 3, label: 'Employee Interview' },
  { id: 4, label: 'Handoff' },
  { id: 5, label: 'Complete' },
]

function VerticalStepper({ currentStep }: { currentStep: OffboardingStep }) {
  return (
    <aside className="w-64 bg-gg-surface border-r border-gg-border flex flex-col py-8 px-6 flex-shrink-0">
      <div className="mb-8">
        <h2 className="text-lg font-bold text-gg-rust">Offboarding</h2>
        <p className="text-xs text-gg-muted mt-1">Knowledge capture pipeline</p>
      </div>
      <nav className="flex-1">
        {STEPS.map((step, i) => {
          const isComplete = step.id < currentStep
          const isCurrent = step.id === currentStep
          return (
            <div key={step.id} className="flex items-start gap-3 mb-1">
              {/* Vertical line + circle */}
              <div className="flex flex-col items-center">
                <div
                  className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0 transition-all ${
                    isComplete
                      ? 'bg-green-500/20 text-green-400'
                      : isCurrent
                        ? 'bg-gg-rust text-white shadow-[0_0_12px_rgba(192,54,44,0.4)]'
                        : 'bg-gg-card text-gg-muted border border-gg-border'
                  }`}
                >
                  {isComplete ? (
                    <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
                    </svg>
                  ) : (
                    step.id
                  )}
                </div>
                {i < STEPS.length - 1 && (
                  <div
                    className={`w-0.5 h-8 my-1 transition-colors ${
                      isComplete ? 'bg-green-500/30' : 'bg-gg-border'
                    }`}
                  />
                )}
              </div>
              {/* Label */}
              <div className="pt-1.5">
                <span
                  className={`text-sm font-medium ${
                    isComplete
                      ? 'text-green-400'
                      : isCurrent
                        ? 'text-gg-text'
                        : 'text-gg-muted'
                  }`}
                >
                  {step.label}
                </span>
              </div>
            </div>
          )
        })}
      </nav>
    </aside>
  )
}

function OffboardingContent() {
  const { step } = useOffboarding()

  return (
    <div className="h-screen flex bg-gg-bg">
      <VerticalStepper currentStep={step} />
      <main className="flex-1 overflow-hidden">
        {step === 1 && <UploadStep />}
        {step === 2 && <AnalysisStep />}
        {step === 3 && <EmployeeStep />}
        {step === 4 && <HandoffStep />}
        {step === 5 && <CompleteStep />}
      </main>
    </div>
  )
}

export default function OffboardingPage() {
  return (
    <OffboardingProvider>
      <OffboardingContent />
    </OffboardingProvider>
  )
}

```

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