Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/assets/banner-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/banner-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions .github/assets/banner.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!doctype html>
<!-- README 배너 원본. 렌더: .github/assets/render-banner.sh -->
<html lang="ko">
<head>
<meta charset="utf-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&family=Manrope:wght@600;800&family=Noto+Sans+KR:wght@500;700&display=block" rel="stylesheet">
<style>
:root {
--paper: 247 244 238; --surface: 255 253 250; --border: 222 215 203;
--text: 31 28 23; --muted: 114 106 94; --tile-off: 231 225 214;
--accent: 28 113 86; --accent-on: 28 113 86; --positive: 63 168 118;
--logo-bg: transparent;
}
html.dark {
--paper: 38 34 32; --surface: 51 45 40; --border: 59 53 47;
--text: 244 239 231; --muted: 164 154 142; --tile-off: 44 38 34;
--accent-on: 127 201 164;
--logo-bg: rgb(247 244 238);
}
* { box-sizing: border-box; margin: 0; }
html, body { width: 1280px; height: 400px; background: rgb(var(--paper)); overflow: hidden; }
body {
font-family: Manrope, 'Noto Sans KR', sans-serif;
color: rgb(var(--text));
display: grid; grid-template-columns: 1fr 470px;
}
.brand { padding: 0 0 0 88px; display: flex; flex-direction: column; justify-content: center; }
.mark { display: flex; align-items: center; gap: 22px; }
.mark img { width: 76px; height: 76px; border-radius: 50%; background: var(--logo-bg); }
.wordmark { font-size: 76px; font-weight: 800; letter-spacing: -0.038em; line-height: 1; }
.sub {
margin: 14px 0 0 98px; font-family: 'IBM Plex Mono', monospace; font-weight: 500;
font-size: 17px; letter-spacing: .03em; color: rgb(var(--muted));
}
.tagline {
margin-top: 40px; font-size: 27px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.45;
}
.tagline em { font-style: normal; color: rgb(var(--accent-on)); }

.art { position: relative; border-left: 1px solid rgb(var(--border)); }
.tiles {
position: absolute; left: 56px; top: 64px; display: grid; gap: 8px;
grid-template-columns: repeat(2, 132px); grid-auto-rows: 86px;
}
.tile { background: rgb(var(--tile-off)); border-radius: 10px; position: relative; }
.tile::after {
content: ''; position: absolute; left: 50%; top: 50%; width: 30px; height: 30px;
transform: translate(-50%, -50%); border-radius: 50%; background: rgb(var(--muted) / .28);
}
.tile.speaking { box-shadow: 0 0 0 3px rgb(var(--positive)); }
.report {
position: absolute; right: 48px; bottom: 52px; width: 250px;
background: rgb(var(--surface)); border: 1px solid rgb(var(--border)); border-radius: 12px;
padding: 20px 22px; box-shadow: 0 18px 40px -18px rgb(0 0 0 / .28);
}
.cap {
font-family: 'IBM Plex Mono', monospace; font-weight: 600; font-size: 12px;
letter-spacing: .08em; color: rgb(var(--accent-on));
}
.row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.row b { font-size: 14px; font-weight: 700; white-space: nowrap; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: rgb(var(--positive)); flex: none; }
.bar { height: 7px; border-radius: 4px; background: rgb(var(--border)); flex: 1; }
html.dark .tile { background: rgb(var(--surface)); }
</style>
</head>
<body>
<section class="brand">
<div class="mark">
<img src="../../apps/frontend/public/logo.svg" alt="">
<span class="wordmark">Convene</span>
</div>
<p class="sub">for Beyond_Imagination</p>
<p class="tagline">링크 하나로 시작하는 화상회의,<br><em>끝나면 회의록까지.</em></p>
</section>
<section class="art">
<div class="tiles">
<div class="tile speaking"></div><div class="tile"></div>
<div class="tile"></div><div class="tile"></div>
</div>
<div class="report">
<p class="cap">MEETING REPORT</p>
<div class="row"><span class="dot"></span><b>요약</b><span class="bar"></span></div>
<div class="row"><span class="dot"></span><b>결정사항</b><span class="bar"></span></div>
<div class="row"><span class="dot"></span><b>액션아이템</b><span class="bar"></span></div>
</div>
</section>
<script>if (location.hash === '#dark') document.documentElement.classList.add('dark');</script>
</body>
</html>
12 changes: 12 additions & 0 deletions .github/assets/render-banner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env sh
# banner.html → banner-light.png / banner-dark.png (2x). CHROME 환경변수로 크롬 경로를 바꿀 수 있다.
set -e
cd "$(dirname "$0")"
CHROME="${CHROME:-chrome}"
SRC="file:///$(pwd -W 2>/dev/null || pwd)/banner.html"

for theme in light dark; do
"$CHROME" --headless=new --disable-gpu --hide-scrollbars --allow-file-access-from-files \
--force-device-scale-factor=2 --window-size=1280,400 --virtual-time-budget=5000 \
--screenshot="$(pwd -W 2>/dev/null || pwd)/banner-$theme.png" "$SRC#$theme"
done
Binary file added .github/assets/shot-home-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-home-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-meeting-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-meeting-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-report-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-report-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-reports-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-reports-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-share-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/shot-share-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 10 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This file is for Claude Code. Read it before editing anything in this repo.
## Source-of-truth docs

- **Notion (external)** — architecture patterns, domain model, layer mapping, sequence + state diagrams (formerly `ARCHITECTURE.md`). Korean, written for the human user. **Ask the user for the link** when you need it; it is not in the repo.
- `CODEBASE_GUIDE.md` (in-repo) — maps the running code to those patterns.
- `DEVELOPMENT.md` (in-repo) — requirements, run, test, contribute. `README.md` is product-facing only.

This file (CLAUDE.md) is the English index for fast context recovery.

Expand All @@ -22,7 +22,7 @@ This file (CLAUDE.md) is the English index for fast context recovery.

## Patterns

- **Backend**: Layered MVC + DDD 4-layer. Dependency direction: Interface → Application → Domain ← Infrastructure. Domain layer has **zero framework imports**. Application talks to Infrastructure only through Ports, and is wired by Nest DI (`@Injectable` + `@Inject(TOKEN)`) — see `CODEBASE_GUIDE.md` §3.4.
- **Backend**: Layered MVC + DDD 4-layer. Dependency direction: Interface → Application → Domain ← Infrastructure. Domain layer has **zero framework imports**. Application talks to Infrastructure only through Ports, and is wired by Nest DI (`@Injectable` + `@Inject(TOKEN)`) — see `DEVELOPMENT.md` "코드 규칙".
- **Frontend**: MVVM. View = `'use client'` component, props only. ViewModel = `useXxxViewModel` hook. Model = zustand store + api service + socket client. **View components do not call `fetch`, `useEffect`, `useState`, socket APIs, or zustand setters directly.**
- **File member order** (deps-first, bottom-up): imports → module constants → supporting types/interfaces/helpers → the file's main class/aggregate/function **last**. Class-internal order (`field → constructor → method`) is enforced by `@typescript-eslint/member-ordering`. Multi-export files with no single namesake (e.g. sibling `*.errors.ts`, wire-type modules) are exempt.

Expand Down Expand Up @@ -50,6 +50,10 @@ This file (CLAUDE.md) is the English index for fast context recovery.
7. **Cross-context coupling via Domain Events (`@nestjs/event-emitter`), or by injecting the other context's Application Service.** Prefer events for notifications (fire-and-forget); inject the service when you need a return value (e.g. `notion/` → `MeetingService.create`). The owning module `exports` that service; **Aggregates and Repositories still never cross a context boundary.** Read-only Value Objects shared by multiple contexts live in `apps/backend/src/shared-kernel/domain/` — this is the DDD **Shared Kernel** pattern. Changes to the shared kernel require alignment from every consumer.
8. **Validation pipe is global** with `whitelist: true, forbidNonWhitelisted: true, transform: true`. Every inbound HTTP/WS payload must be a DTO class.
9. **Comments say only what the code cannot.** One or two lines; delete a block that restates the type, the folder, or the next line rather than rewording it. **Never put measurement numbers, experiment conclusions, or design history in code, specs, or `.env.template`** — a tuned constant gets "what it is", not "why this value and what we tried". Those belong in the experiment report artifact (ask the user for the link). Same rule for memory files: they carry the decision and where to look, not the data behind it.
10. **`useFactory` only when wiring needs a runtime value** (env config, conditional fallback); static bindings use `useClass`/`useExisting`.
11. **Test doubles for concrete classes use `stub<T>()`** (`shared-kernel/testing/stub.ts`), never `as unknown as T`.
12. **Renaming a DI token silently disables e2e `.overrideProvider()`** (real external API calls) — move the overrides in `apps/backend/test/*.e2e-spec.ts` with it.
13. **WS gateways validate with `wsValidationPipe()` only** — never `new ValidationPipe(...)` directly.

## TDD cycle (per non-trivial unit)

Expand Down Expand Up @@ -83,7 +87,7 @@ After each step, summarize in Korean to the user and wait for confirmation.

```
convene/
├── CLAUDE.md, README.md, CODEBASE_GUIDE.md (scope·architecture → Notion)
├── CLAUDE.md, README.md, DEVELOPMENT.md (scope·architecture → Notion)
├── package.json, pnpm-workspace.yaml, turbo.json, tsconfig.base.json
├── eslint.config.mjs, .prettierrc, .editorconfig, .gitignore
├── apps/
Expand Down Expand Up @@ -118,12 +122,12 @@ feature/<name>/
Model (zustand stores, api fetch, socket clients) is shared across features and lives in
`src/shared/{stores,api,socket}`, not per-feature.

**File granularity**: split by "what you edit together", not by count — see `CODEBASE_GUIDE.md`
섹션#1. Long files are fine; hopping between files is not. Subfolders only when they hold 2+ files.
**File granularity**: split by "what you edit together", not by count — see `DEVELOPMENT.md`
"코드 규칙". Long files are fine; hopping between files is not. Subfolders only when they hold 2+ files.

## When you (Claude) come back to this repo cold

1. Read `CLAUDE.md` (this file) → `CODEBASE_GUIDE.md`. Scope/architecture detail lives in Notion (ask the user for the link).
1. Read `CLAUDE.md` (this file) → `DEVELOPMENT.md`. Scope/architecture detail lives in Notion (ask the user for the link).
2. Check `git log -10 --oneline` for recent progress.
3. Locate the current task: failing specs, TODO comments, or ask the user.
4. **Speak Korean to the user.** Code identifiers (class/function/variable/event names, file paths) stay English. **All code comments, JSDoc bodies, and test (`it`/`describe`) labels are written in Korean** so non-Claude collaborators can read them. The Notion docs stay Korean. This file (CLAUDE.md) stays English because it is for future Claude sessions.
Expand Down
Loading
Loading