# Project export: bomti

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: AI career editor that turns self-introduction drafts into clear, evidence-based stories.
- Devpost: https://devpost.com/software/a-restless-youth
- GitHub: https://github.com/yt010108/bomti
- Video: https://www.youtube.com/embed/3AdXwAoC6nE?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — yt010108 (37 commits), ryuhyangoh (36 commits)

## Devpost submission (written by the team)

### Inspiration

Writing a strong self-introduction or interview answer is difficult because people often know what they did, but struggle to explain the context, evidence, and impact clearly. We wanted to create a career-writing tool that helps applicants improve their own stories without replacing their voice with generic AI text. Bomti was inspired by the idea of an AI career editor: a tool that points out vague claims, missing evidence, and weak connections between actions and results, then gives practical directions for revision.

### What it does

Bomti analyzes self-introduction and interview answers against five dimensions: Context fit Specificity Evidence and credibility Generic or cliché expressions Tone and readability Users enter a question, their answer, target role, and optional company context. Bomti returns an overall score, dimension-level feedback, supporting sentence evidence, and concrete improvement directions. Guest users can try the diagnostic flow with limited usage. Logged-in users can save, review, and delete their evaluation history.

### How we built it

We built Bomti with Next.js, React, and TypeScript. The interface is designed as a calm editorial workspace, with a dedicated landing page, diagnostic form, results dashboard, history page, and account settings page. For persistence and privacy-sensitive data handling, we designed the project around Supabase and PostgreSQL with row-level security policies. The project includes database migrations, generated database types, fixtures, and integration checks for tenant isolation and account deletion behavior. We also built automated checks for: Input validation and consent requirements Guest limits and provider failure states XSS-safe evidence rendering Saved evaluation history and deletion flows Account deletion lifecycle behavior Responsive layouts and accessibility Score boundaries from 0 to 100

### Challenges we ran into

The biggest challenge was balancing useful feedback with privacy and user control. Career answers can contain personal experiences, employer information, and sensitive context, so we designed the flow to avoid treating the user’s original answer as disposable training data. Another challenge was making the interface feel helpful rather than judgmental. A score alone is not actionable, so we paired every result with dimension-level explanations, evidence references, and revision directions. We also had to keep the UI redesign aligned with the product’s existing behavioral contracts. As the experience evolved from a single evaluation form into separate landing, diagnosis, result, history, and account screens, we updated browser tests to verify submissions, cancellations, XSS protection, deletion, and responsive behavior end to end.

### What we learned

We learned that trustworthy AI writing tools need more than a polished result screen. They need clear consent, transparent limits, safe failure states, and a way for users to control their data. We also learned how valuable automated visual and accessibility checks are during UI iteration. They helped us preserve keyboard access, score-meter semantics, responsive layouts, and user-facing flows while making large design changes.

### What's next

for Bomti Next, we want to add more role-specific feedback, richer revision workflows, and a production deployment with a fully configured authentication and Supabase environment. Our goal is to make career writing feel less like guessing what recruiters want and more like clearly communicating real experience.

## README (from the GitHub repository)

# Bomti

Bomti는 취업 준비생의 자기소개서 답변이 질문·직무·회사 또는 공고 맥락에 비해 얼마나 상투적이거나 과장됐는지 판단하는 한국어 웹 서비스다. 답변을 대신 작성하지 않고, 높은 점수일수록 더 밤티인 0–100 지수와 근거 문장·설명·개선 방향을 제공한다.

## 현재 구현 기준

승인된 요구사항은 [요구사항 원장](docs/requirements.md)에, 변경 불가한 실행 제약은 [Handoff](Handoff.md)와 승인 계획에 있다. 지금은 계획 순서에 맞춰 로컬 검증 기반부터 구현 중이며, 실제 배포·Google OAuth 설정·유료 모델 호출·외부 데이터 import·push/PR은 별도 운영자 승인이 있어야만 실행한다.

## 사용자 경험

1. 사용자는 질문, 답변, 목표 직무, 회사/공고 맥락을 입력한다.
2. 모든 필수 동의를 직접 선택한다.
3. 입력은 문장 세그먼트화와 가명처리를 거친 뒤에만 모델로 전달된다.
4. 결과는 Bomti 지수, 다섯 위험 차원, 문장 근거, 설명, 제한된 개선 방향을 보여 준다.
5. 비로그인은 하루 한 번의 비영속 미리보기를 받고, Google 인증 사용자는 캠페인당 세 번의 상세 평가와 삭제 가능한 이력을 가진다.

공개 서비스는 답변 하나를 평가한다. 답변 쌍 비교와 사람 평점은 익명 내부 벤치마크 보정에만 사용하며 공개 기능이나 관리자 대시보드가 아니다.

## 안전 경계

- 동의 → 세그먼트화·가명처리 → 모델 전송 순서를 바꾸지 않는다.
- 원문은 로그, DB, 증거에 저장하지 않는다.
- 공급자 장애·예산 부족·검증 실패 시 다른 유료 모델로 자동 대체하지 않는다.
- 실제 공급자, OAuth, Supabase, Vercel의 변경은 운영자 입력과 명시적 승인이 있을 때만 가능하다.

## 로컬 시작

```bash
npm ci
npm run typecheck
npm run lint
npm run test -- --run
npm run build
```

세부 계약, 예정된 API/화면, 증거 규칙은 [docs/requirements.md](docs/requirements.md)를 따른다.


## Detected evidence (automated analysis)

Indexed codebase: 138 recognized source files, 684 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vercel (technology) — detected in the code
- HTML (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- PostgreSQL (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 182)

```
.codex/config.toml
.env.example
.env.test.example
.github/workflows/ci.yml
.github/workflows/database.yml
.gitignore
.npmrc
.omo/drafts/bomti-product-goal-and-scaffolding.md
.omo/plans/bomti-product-goal-and-scaffolding.md
AGENTS.md
app/%5Fshowcase/page.tsx
app/%5Fshowcase/showcase-client.tsx
app/account/page.tsx
app/api/account/route.ts
app/api/auth/cancel/route.ts
app/api/auth/refresh/route.ts
app/api/auth/sign-in/route.ts
app/api/auth/sign-out/route.ts
app/api/evaluations/[id]/feedback/route.ts
app/api/evaluations/[id]/route.ts
app/api/evaluations/route.ts
app/api/health/route.ts
app/api/usage/route.ts
app/auth/callback/route.ts
app/dev-client-instrumentation.tsx
app/diagnosis/page.tsx
app/evaluation-form.tsx
app/globals.css
app/history/[id]/history-detail-client.tsx
app/history/[id]/page.tsx
app/history/history-client.tsx
app/history/page.tsx
app/landing-page.tsx
app/layout.tsx
app/page.tsx
components/bomti/button.tsx
components/bomti/consent-control.tsx
components/bomti/evaluation-result.tsx
components/bomti/evidence-card.tsx
components/bomti/form-field.tsx
components/bomti/index.ts
components/bomti/score-meter.tsx
components/bomti/status-banner.tsx
data/benchmark/synthetic-pairs.json
DESIGN.md
docs/architecture.md
docs/benchmark-pipeline.md
docs/demo-scenario.md
docs/operations.md
docs/product-plan.md
docs/requirements.md
docs/roadmap.md
docs/rubric.md
eslint.config.mjs
EXECUTION_POLICY.md
Handoff.md
lib/api/contract.ts
lib/api/service.ts
lib/auth/fixture-auth.ts
lib/config/env.ts
lib/contracts/evaluation.ts
lib/contracts/text.ts
lib/contracts/verdict-candidates.ts
lib/contracts/verdict-normalized.ts
lib/contracts/verdict-shared.ts
lib/contracts/verdict-sol.ts
lib/contracts/verdict-validation.ts
lib/contracts/verdict.ts
lib/database/deletion.ts
lib/database/generated.types.ts
lib/dataset/jsonl.ts
lib/dataset/package.json
lib/judge/aggregation.ts
lib/judge/calibration.ts
lib/judge/merge.ts
lib/judge/orchestrator.ts
lib/judge/package.json
lib/judge/score.ts
lib/judge/sol-fields.ts
lib/operations/readiness.ts
lib/privacy/boundaries.ts
lib/privacy/detect.ts
lib/privacy/eligibility.ts
lib/privacy/index.ts
lib/privacy/logger.ts
lib/privacy/pseudonymize.ts
lib/privacy/types.ts
lib/providers/adapter-helpers.ts
lib/providers/config.ts
lib/providers/cost.ts
lib/providers/errors.ts
lib/providers/fake.ts
lib/providers/index.ts
lib/providers/openai.ts
lib/providers/opencode.ts
lib/providers/prompt.ts
lib/providers/schemas.ts
lib/providers/server-only.ts
lib/providers/transport.ts
lib/providers/types.ts
lib/providers/wire.ts
lib/usage/cost.ts
lib/usage/encryption.ts
lib/usage/identity.ts
lib/usage/index.ts
next-env.d.ts
next.config.ts
package.json
playwright.config.ts
prompts/judge-v1.md
README.md
scripts/evidence/bind.mjs
scripts/evidence/lane-contract.mjs
scripts/evidence/lane.mjs
scripts/evidence/manifest.mjs
scripts/evidence/receipt.mjs
scripts/preflight/providers.mjs
scripts/verification/api.mjs
scripts/verification/benchmark.ts
scripts/verification/database.mjs
[62 more files omitted for size]
```

### Dependencies

- package.json: @axe-core/playwright@4.12.1, @next/eslint-plugin-next@15.5.20, @playwright/test@1.61.1, @types/node@22.15.0, @types/react@19.2.7, @types/react-dom@19.2.3, @typescript-eslint/parser@8.64.0, eslint@9.39.5, next@15.5.20, react@19.2.7, react-dom@19.2.7, remark-gfm@4.0.1, remark-parse@11.0.0, supabase@2.109.1, typescript@5.9.3, unified@11.0.5, vercel@56.3.2, vitest@4.1.10, zod@3.25.76

### Recent commits (newest first)

- Merge pull request #40 from yt010108/agent/stitch-ui-refresh
- fix(ui): restore score and disclosure contracts
- Align Bomti UI with Stitch export
- Refresh Bomti UI from Stitch direction
- feat: complete production operations verification (#39)
- feat(benchmark): add paired corpus pipeline
- feat(auth): Google fixture 인증·이력·계정 삭제 saga
- feat(web): 접근 가능한 Bomti 결과 화면
- feat(web): 공개 평가 입력과 동의 UI 추가
- feat(api): 검증된 평가와 소유 history API 추가
- feat(judge): 하이브리드 평가 오케스트레이션 구현
- feat(providers): 정규화된 모델 어댑터와 capability probe 추가
- Merge pull request #24 from yt010108/feat/issue-21-usage-budget
- feat(usage): enforce atomic quotas and budgets
- Merge pull request #23 from yt010108/feat/issue-20-privacy-pipeline
- feat(privacy): add pseudonymization and benchmark eligibility gates
- Merge pull request #22 from yt010108/feat/issue-19-design-system
- feat(ui): establish Bomti design system and primitives
- Merge pull request #8 from yt010108/agent/supabase-persistence
- Merge pull request #17 from yt010108/fix/issue-13-verification-runners

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

### AGENTS.md

```markdown
# Bomti Repository Agent Guidance

- Before implementation or review, read `EXECUTION_POLICY.md` and apply its model, reasoning, delegation, and context rules.
- Treat `Handoff.md`, `.omo/plans/bomti-product-goal-and-scaffolding.md`, and `.omo/drafts/bomti-product-goal-and-scaffolding.md` as the immutable approved planning lock. Do not edit them during implementation.
- The approved plan's acceptance criteria, privacy boundaries, evidence requirements, and F1-F4 final gates remain mandatory. `EXECUTION_POLICY.md` changes resource allocation, not product scope or proof requirements.
- Preserve unrelated work, including existing `.omo/evidence/` and `.omo/ulw-loop/` state. Do not delete, rewrite, or commit it unless the active task explicitly owns it.
- An explicit user request for a different model, reasoning level, review depth, or delegation strategy overrides the defaults for that task only.

```

### EXECUTION_POLICY.md

```markdown
# Bomti Token-Efficient Execution Policy

## Purpose

This policy reduces total Codex token use while preserving the approved Bomti implementation plan and its verification gates. It is an operational overlay only. It does not amend product decisions, schemas, security boundaries, acceptance criteria, evidence contracts, or the immutable planning lock.

## Default allocation

- Use `gpt-5.6-terra` with `medium` reasoning for routine repository discovery, documentation, bounded implementation, test execution, and ordinary fixes.
- Use `low` reasoning for deterministic searches, file inventory, formatting, copy changes, and mechanical checks when the selected surface supports it.
- Prefer one direct agent for a task that can be completed safely in one context.
- Keep responses concise. Store full logs outside the conversation and return only the outcome, failing assertion, relevant excerpt, and file reference.

## When Sol is justified

Use `gpt-5.6-sol` only when at least one condition applies:

- architecture or product-contract ambiguity cannot be resolved from the approved plan;
- authentication, authorization, RLS, privacy, deletion, reconciliation, budget, or concurrency logic is being designed or reviewed;
- Judge orchestration, score provenance, provider partial acceptance, or refund behavior crosses multiple modules;
- a reproducible defect remains unexplained after two materially different diagnostic attempts;
- the approved plan explicitly requires an independent high-accuracy or security review;
- F1-F4 final verification or release-readiness judgment is running.

Use `high` reasoning for these cases. Use `xhigh`, `max`, or `ultra` only when the task explicitly requires it or a prior high-reasoning pass reports unresolved material risk.

## Codex-only execution

- Do not use LazyCodex, LazyCodex-specific worker/reviewer/gate roles, or LazyCodex/OMO orchestration commands and modes, including `omo:*`, `$start-work`, `ultrawork`, `ulw-loop`, `ulw-research`, and `review-work`.
- Use the primary Codex agent directly for discovery, implementation, verification, review, and handoff. Do not translate routine work into a LazyCodex team or review pipeline.
- If independent parallel work is genuinely necessary, use ordinary Codex subagents only, assign bounded non-overlapping ownership, and avoid duplicate exploration. Never select a LazyCodex-labelled role.
- Execute the smallest ready Todo from the approved plan and run its targeted checks and evidence contract. Do not repeat full-plan review after each Todo; reserve broad review for the end of a meaningful wave or an explicit plan gate.
- Keep full logs in artifact files and summarize only failures, causes, and relevant paths in the main thread.

## Context discipline

- Start with `rg`, targeted file ranges, named tests, and the exact paths referenced by the active Todo. Do not reread the whole repository when the approved plan already identifies the surface.
- Keep `AGENTS.md` concise and lo
[truncated — 1295 more characters]
```

### package.json

```
{
  "name": "bomti",
  "version": "0.1.0",
  "private": true,
  "engines": {
    "node": ">=22.0.0"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "typecheck": "tsc --noEmit",
    "lint": "eslint . --max-warnings=0",
    "test": "vitest run",
    "test:db": "node scripts/verification/database.mjs",
    "test:e2e": "node scripts/verification/fixture.mjs e2e",
    "test:privacy": "node scripts/verification/privacy.mjs",
    "test:usage": "node scripts/verification/database.mjs",
    "test:providers": "node scripts/verification/providers.mjs",
    "preflight:providers": "node scripts/preflight/providers.mjs",
    "test:judge": "node scripts/verification/judge.mjs",
    "test:api": "node scripts/verification/api.mjs",
    "visual:qa": "node scripts/verification/visual.mjs",
    "qa:final": "node scripts/verification/fixture.mjs final-qa",
    "evidence:lane": "node scripts/evidence/lane.mjs",
    "verify:requirements": "node scripts/verification/requirements.mjs",
    "verify:traceability": "node scripts/verification/fixture.mjs traceability",
    "verify:security": "node scripts/verification/fixture.mjs security",
    "verify:scope": "node scripts/verification/fixture.mjs scope",
    "verify:ops": "node scripts/verification/operations.mjs",
    "verify:live": "node scripts/verification/live.mjs",
    "verify:independent-review": "node scripts/verification/fixture.mjs independent-review",
    "env:check": "node scripts/verification/env-check.mjs",
    "dev:test": "next dev",
    "benchmark:validate": "node --experimental-strip-types scripts/verification/benchmark.ts validate",
    "benchmark:pair": "node --experimental-strip-types scripts/verification/benchmark.ts pair",
    "benchmark:curate": "node --experimental-strip-types scripts/verification/benchmark.ts curate",
    "benchmark:import": "node --experimental-strip-types scripts/verification/benchmark.ts import",
    "benchmark:export": "node --experimental-strip-types scripts/verification/benchmark.ts export",
    "benchmark:report": "node --experimental-strip-types scripts/verification/benchmark.ts report"
  },
  "dependencies": {
    "next": "15.5.20",
    "react": "19.2.7",
    "react-dom": "19.2.7",
    "zod": "3.25.76"
  },
  "devDependencies": {
    "@axe-core/playwright": "4.12.1",
    "@next/eslint-plugin-next": "15.5.20",
    "@playwright/test": "1.61.1",
    "@typescript-eslint/parser": "8.64.0",
    "@types/node": "22.15.0",
    "@types/react": "19.2.7",
    "@types/react-dom": "19.2.3",
    "eslint": "9.39.5",
    "supabase": "2.109.1",
    "typescript": "5.9.3",
    "unified": "11.0.5",
    "vercel": "56.3.2",
    "vitest": "4.1.10",
    "remark-gfm": "4.0.1",
    "remark-parse": "11.0.0"
  }
}

```

### lib/dataset/package.json

```
{
  "type": "module"
}

```

### lib/judge/package.json

```
{
  "type": "module"
}

```

### scripts/verification/package.json

```
{
  "type": "module"
}

```

### app/page.tsx

```typescript
import { EvaluationForm } from "./evaluation-form";
import { LandingPage } from "./landing-page";

export default async function Home({
  searchParams
}: {
  searchParams: Promise<{ fixture?: string; scenario?: string }>;
}) {
  const { fixture, scenario } = await searchParams;
  const fixtureEnabled = process.env.BOMTI_API_TEST_MODE === "true";
  const score = process.env.NODE_ENV === "development" && /^result-(?:0|24|25|49|50|74|75|100)$/.test(scenario ?? "")
    ? Number(scenario?.slice("result-".length))
    : undefined;
  if (score === undefined && !fixtureEnabled) return <LandingPage />;

  return <EvaluationForm fixtureEnabled={fixtureEnabled} fixtureAudience={fixtureEnabled && fixture === "auth" ? "authenticated" : "guest"} fixtureScenario={fixtureEnabled ? scenario : undefined} fixtureResultScore={score} />;
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { DevClientInstrumentation } from "./dev-client-instrumentation";
import "./globals.css";

export const metadata: Metadata = {
  title: "Bomti",
  description: "자기소개서 답변의 상투성과 맥락 위험을 점검하는 한국어 평가 도구"
};

export default function RootLayout({
  children
}: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="ko">
      <body data-bomti-fixture={process.env.BOMTI_API_TEST_MODE === "true" ? "true" : "false"}>
        {process.env.NODE_ENV === "development" ? <DevClientInstrumentation /> : null}
        <header className="bomti-site-header">
          <div className="bomti-site-header__inner">
            <a className="bomti-brand" href="/" aria-label="Bomti 홈">Bomti</a>
            <nav className="bomti-site-nav" aria-label="주요 메뉴">
              <a href="/diagnosis">진단하기</a>
              <a href="/history">내 기록</a>
            </nav>
            <a className="bomti-site-header__cta" href="/account">계정 설정</a>
          </div>
        </header>
        {children}
        <footer className="bomti-site-footer">
          <div className="bomti-site-footer__inner">
            <div className="bomti-site-footer__brand"><strong>Bomti</strong><span>Bomti index는 합격·불합격 판정이 아닙니다.</span></div>
            <nav aria-label="서비스"><a href="/diagnosis">평가와 개인정보 안내</a><a href="/history">내 기록</a><a href="/account">계정 설정</a></nav>
          </div>
        </footer>
      </body>
    </html>
  );
}

```

### app/landing-page.tsx

```typescript
const steps = [
  { number: "STEP 01", title: "입력", body: "자기소개서와 면접 답변을 원문의 결을 유지한 채 차분히 살펴봅니다.", icon: "⌁" },
  { number: "STEP 02", title: "AI 진단", body: "맥락, 근거, 구체성과 표현의 흐름에서 비어 있는 지점을 찾습니다.", icon: "✦" },
  { number: "STEP 03", title: "개선 방향 확인", body: "다음 문장을 더 설득력 있게 만드는 실질적인 방향을 제안합니다.", icon: "✓" }
] as const;

export function LandingPage() {
  return (
    <main className="bomti-landing">
      <section className="bomti-landing-hero bomti-container">
        <div className="bomti-landing-hero__copy">
          <p className="bomti-editor-label"><span aria-hidden="true">✿</span> Career Editor</p>
          <h1>고치기 전에,<br />맥락과 근거부터 확인하세요</h1>
          <p className="bomti-landing-hero__lead">자기소개서에서 모호하거나 상투적인 표현, 근거가 부족한 문장을 찾아 더 구체적인 개선 방향을 제안합니다.</p>
          <div className="bomti-landing-hero__actions">
            <a className="bomti-button" href="/diagnosis">내 답변 진단하기 <span aria-hidden="true">→</span></a>
            <p className="bomti-landing-privacy"><span aria-hidden="true">♙</span><span>Privacy-first processing.<br />명시적 동의 후 가명처리된 입력만 평가 모델에 전송합니다.</span></p>
          </div>
        </div>
        <div className="bomti-landing-visual" role="img" aria-label="노트북이 놓인 차분한 작업 공간과 답변 분석 진행 카드">
          <div className="bomti-diagnosis-progress">
            <div><span aria-hidden="true">▣</span><strong>AI DIAGNOSIS RUNNING</strong></div>
            <i><b /></i>
            <p><span>Scanning context...</span><span>67%</span></p>
          </div>
        </div>
      </section>

      <section className="bomti-landing-steps">
        <div className="bomti-container">
          <header><h2>How it works</h2><p>좋은 글을 대신 쓰지 않고, 더 설득력 있는 나의 이야기를 만들 수 있도록 돕습니다.</p></header>
          <ol>
            {steps.map((step) => <li key={step.number}><span className="bomti-step-icon" aria-hidden="true">{step.icon}</span><p>{step.number}</p><h3>{step.title}</h3><span>{step.body}</span></li>)}
          </ol>
        </div>
      </section>
    </main>
  );
}

```

### app/history/page.tsx

```typescript
import { HistoryClient } from "./history-client";

export default function HistoryPage() {
  return <HistoryClient />;
}

```

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