Skip to content

Commit a87e435

Browse files
jay-swkclaude
andcommitted
feat(onboarding): npm run init 한 줄 + 단일 HTML 가이드 (PRD §11 H4 받침)
8단계 onboarding(clone→install→build→capture install→cp snippet→Claude 재시작→serve→browser)을 1 명령 + 1 재시작으로 압축. - bin/awm.mjs: installClaudeHook({autoMerge}) + autoMergeClaudeSettings (eventName별 깊이 머지 + command+matcher dedup + corrupt JSON throw) - scripts/awm-init.sh: Node ≥ 20 check / npm install(.package-lock.json 존재 skip) / build(dist 있으면 skip) / capture install --auto-merge / PID 파일 체크 후 serve 백그라운드 / 5단계 색깔 출력 + EOF 안내 박스 - package.json: npm run init 추가 - docs/tester-quickstart.html: self-contained 250줄 — 3 단계 + FAQ 4건 + 트러블슈팅 5건, inline CSS prefers-color-scheme 다크 자동, §12.3 외부 노출 금지 7항 통과 - CLAUDE.md Build & Verify — 테스터 한 줄 섹션 + 캡처 hook 간소화 - README.md 최상단 Quick Start 5분 박스 + 가이드 링크 검증: clean state → init 5/5 PASS / 두 번째 init idempotent / curl /api/health ok / node 40/40 + web 67/67 + build 175ms. Evaluator FAIL 2건 즉시 해소 (HTML "1인 베타" §12.3 위반 제거, npm install 멱등 로직 .package-lock.json 체크로 변경). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 18dab24 commit a87e435

8 files changed

Lines changed: 484 additions & 18 deletions

File tree

CLAUDE.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,23 @@ v1에서 *Vite 8 + React 18 + TS 5.7 SPA + Node ESM CLI + 로컬 `.awm/`*를 사
3030

3131
M0 Tech Validation 진입(2026-05-13) 후 v1 자산이 main에 통합돼 *로컬 작동*. Stack 결정(M1)은 별개.
3232

33+
### 외부 테스터 / 신규 환경 — 한 줄
34+
35+
```bash
36+
npm run init # Node check + install + build + hook 자동 머지 + serve 백그라운드
37+
# → Claude Code 재시작 후 http://127.0.0.1:5173/today
38+
```
39+
40+
브라우저 가이드: `open docs/tester-quickstart.html` (self-contained HTML, 5분 onboarding).
41+
3342
### 빌드·테스트
3443

3544
```bash
3645
npm install # 첫 1회 (82 pkg, 0 vuln)
3746
npm run build # vite8 — web/ → dist/ (~170ms)
3847
cd web && npm test -- --run # web 67 case (jsdom)
3948
npx vitest run # root 40 case (hashchain 17 + match/persist/github)
40-
cd web && npx tsc --noEmit # TS strict
49+
cd web && npx tsc --noEmit # TS strict (루트엔 tsconfig 없음 — web/ 안에서)
4150
```
4251

4352
### 로컬 서버 운영 (백그라운드 PID 관리)
@@ -65,23 +74,17 @@ node bin/awm.mjs audit show --last 5 # 최근 N개의 prev/hash 디버그 출
6574

6675
### 캡처 hook (S2 측정 시작 시 1회)
6776

68-
```bash
69-
# 1) Claude Code hook 생성
70-
node bin/awm.mjs capture install claude
71-
# → .awm/hooks/claude-capture-hook.mjs (실행권한 자동)
72-
# → .awm/claude-settings-snippet.json (SessionStart/UserPromptSubmit/PreToolUse(Bash|Edit|Write)/PostToolUse/SessionEnd 5종)
73-
74-
# 2) snippet을 settings.local.json 에 복사 (settings.json X — hook 절대경로가 내 머신 한정이라 공유 시 다른 사용자 매 prompt 에러)
75-
cp .awm/claude-settings-snippet.json .claude/settings.local.json
76-
# .gitignore에 .claude/settings.local.json 등재됨
77-
78-
# 3) Claude Code 재시작 — 현재 세션에는 적용 안 됨, 다음 세션부터 events.jsonl 누적
77+
대부분 `npm run init` 이 처리. 수동/세부 제어가 필요할 때:
7978

80-
# 4) git pre-commit hook (선택)
81-
node bin/awm.mjs capture install git-hooks # .git/hooks/pre-commit, git-tracked X
79+
```bash
80+
node bin/awm.mjs capture install claude --auto-merge # snippet + .claude/settings.local.json 자동 머지 (idempotent)
81+
node bin/awm.mjs capture install claude # snippet만 생성 (수동 머지)
82+
node bin/awm.mjs capture install git-hooks # .git/hooks/pre-commit (git-tracked X)
8283
```
8384

84-
설치 확인: 다음 세션 시작 후 `node bin/awm.mjs audit show --last 3` — SessionStart 이벤트가 prev/hash와 함께 보이면 OK.
85+
**왜 settings.local.json인가**: hook 경로가 *내 머신 절대경로*이므로 `.claude/settings.json`(프로젝트 공유)에 넣으면 다른 사용자 clone 시 매 prompt 에러. `.gitignore``.claude/settings.local.json` 등재됨.
86+
87+
설치 확인: Claude Code 재시작 후 `node bin/awm.mjs audit show --last 3` — SessionStart 이벤트가 prev/hash와 함께 보이면 OK.
8588

8689
## Non-Negotiables (advisory — 강제 미설치)
8790
- `.env`, `.secret/`, `*.pem`, `*.key`, 액세스 토큰을 커밋·로그·이슈에 노출하지 않는다.

NOVA-STATE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
||||
7070

7171
## Last Activity
72+
- Tester Onboarding — `npm run init` 한 줄 + `docs/tester-quickstart.html` 단일 HTML 가이드 + `capture install claude --auto-merge` 자동 머지 → PASS(qa + Evaluator, FAIL 2건 즉시 해소). 사용자 지적 *"8단계 onboarding이 외부 테스터에 비현실적"* → 5분 first-value 압축. **영향 7 파일**: (1) `bin/awm.mjs` — `installClaudeHook({autoMerge})` + `autoMergeClaudeSettings(snippet)` 신규 함수, settings.local.json 깊이 머지(eventName별 그룹 append, command+matcher 기준 dedup) + corrupt JSON throw + CLI `--auto-merge` 옵션 (2) `scripts/awm-init.sh` 신규 — Node ≥ 20 check, npm install(node_modules/.package-lock.json 존재 시 skip), build(dist 있으면 skip), capture install --auto-merge, PID 파일 직접 체크 후 serve 백그라운드, 5단계 색깔 출력 + 마지막 안내 박스 (3) `scripts/awm-serve.sh` (기존 그대로) (4) `package.json` — `npm run init` 추가 (5) `docs/tester-quickstart.html` 신규 self-contained 250줄 — 3 단계(init/Claude 재시작/첫 세션) + FAQ 4건(데이터 위치·hook 영향·종료·제거) + 트러블슈팅 5건(Node·포트·audit 비어있음·브라우저·가이드 stale), inline CSS prefers-color-scheme 다크 자동, Toss-style 외부 페이지 톤(.pub-* 정신), §12.3 외부 노출 금지 7항 통과 (6) `CLAUDE.md` Build & Verify — "외부 테스터 / 신규 환경 — 한 줄" 섹션 + 캡처 hook 섹션 --auto-merge primary로 간소화 (7) `README.md` 최상단 Quick Start 5분 박스 + tester-quickstart.html 링크. **검증**: clean state → npm run init 5/5 PASS + PID 출력 + EOF 박스 / 두 번째 init idempotent(install skip + build skip + hooks "5 already present" + serve "이미 실행 중") / curl /api/health ok / node 40/40 + web 67/67 + tsc clean + build 175ms. **Evaluator FAIL 2건 즉시 해소**: FAIL-1 HTML footer "1인 베타" §12.3 위반 → 제거(외부 노출 금지 7항 7번 운영 인원 규모) / FAIL-2 npm install 멱등 로직 오작동(node_modules -nt 비교 역전) → node_modules/.package-lock.json 존재 체크로 변경. Evaluator 미커버: Node < 20 fail path 실제 실행, Windows, 다크 모드 브라우저 렌더. 산출물: `docs/projects/plans/tester-onboarding.md` + `docs/tester-quickstart.html`. **PRD §11 H4 5분 first-value 가설을 받칠 onboarding 인프라 완비** (8단계 → 1 명령 + 1 재시작). | 2026-05-13
7273
- 개발 편의 — `scripts/awm-serve.sh` (start/stop/restart/status/logs) + npm scripts(`serve`, `serve:stop|restart|status|logs`) 추가 → PASS. 사용자 요청 "로컬 확인용 서버 관리 스크립트". PID/log는 `.awm/serve.{pid,log}` (gitignored). start는 dist 없으면 build → ingest --limit 30 → serve --port 5173 백그라운드. stop은 SIGTERM 5회 폴링 후 SIGKILL fallback. 검증: status idle → start(PID 39501 시작) → curl /api/health ok → restart(PID 51389) → stop → status idle → logs 정상. 환경변수 AWM_PORT / AWM_INGEST_LIMIT override 가능. | 2026-05-13
7374
- M0/S3 H2-b 변조 5/5 명령 수준 측정 (데이터 의존성 0, S2 무관 선행 PASS) → PASS. 메인 push 직후 main checkout 상태에서 자율 진행. Plan H2-b 정의 *"events.jsonl 1행 변조 시 verify 명령 5회 반복 5/5 탐지 + 변조 위치 출력"* 충족. **절차**: (1) capture sample ×6 → events.jsonl 6행 + audit chain ok 확인 (2) `/tmp/h2b-baseline.jsonl` 백업 (3) 시나리오 5건 직렬: 필드 수정(index 0 summary) → brokenAt=0 hash mismatch / 레코드 삽입(index 2 fake) → brokenAt=2 prev mismatch(expected 2a1bf93d, got 00000000) / 레코드 삭제(index 1) → brokenAt=1 prev mismatch / 순서 swap(2-3) → brokenAt=2 prev mismatch / hash 직접 조작(index 4 → ff×64) → brokenAt=4 hash mismatch. 매 시나리오마다 baseline 복원→변조→verify→exit=1 확인 (4) 최종 baseline 복원 → `Audit chain OK — 6 events verified.` exit 0. **5/5 모두 정확** + 사유 type(hash mismatch vs prev mismatch) 적절히 분기. S1.5 unit 17 case와 *두 층위*(unit + CLI 통합)에서 변조 탐지. **의미**: H2-b는 S2 dogfooding 데이터 의존성 0이라 *S3 일부를 선행으로 닫음*. H1·H2-a·H3는 S2 1주 후 측정 대기. `docs/verifications/m0-h2b-cmd-tampering.md` 신규 + NOVA-STATE Tasks 분리(S3 H2-b done, S3 H1·H2-a·H3 pending). .awm 측정 잡음 정리 후 working tree clean. | 2026-05-13
7475
- M0/S1.7 SessionDetail seed-id mismatch fix (NOVA-STATE Blocker #2 해소) → PASS. 위임 받은 자율 진행으로 S1.5 직후 연속 수행. **변경 3 파일**: (1) `web/src/screens/SessionDetail.tsx` — `useIngest()` 추가 + `liveSession = ingestSessions.find(s => s.id === id)` 우선 lookup + `seedSession = SESSIONS.find` fallback + `session = liveSession ?? seedSession` + `isLive = Boolean(liveSession) && !seedSession`로 seed s-024 풀데이터 우선 보존 + loading=true && !session 분기에서 role=status "세션 데이터 불러오는 중…" 표시 + isLive 분기에서 page-h 헤더(intent/tool/actor/repo/when) + role=status 안내 카드("실 캡처 세션 — 시안 상세 데이터 미연결") + 기본 메타 dl(도구/작업자/레포/의도/상태/파일·명령 6 row) 표시, SESSION_DETAIL은 isLive=false 분기에서만 사용 (2) `web/src/test/setup.ts` — useIngest 글로벌 vi.mock stub을 `vi.fn(() => ({...}))` 형식으로 추가하여 case별 mockReturnValueOnce 가능 (3) `web/src/App.test.tsx` — S1.7 케이스 2건 추가: 실 ingest id `claude_abc_flow08_4b8bb4` → 실 캡처 카드 + role=status + 기본 메타 heading / loading=true → 로딩 안내 status. **분기 4종**: s-024 → 풀데이터 / 다른 s-NNN → s-024 데이터 + mock 한계 배지(기존) / claude_XXX → 실 캡처 카드 + dl 기본 메타 / loading && match X → 로딩 안내. **검증**: web 67/67 (기존 65 + S1.7 2) + node 40/40 회귀 + build 173~204ms + tsc clean. **Evaluator(qa-engineer) PASS** Medium #1(loading=true 자동화 누락) 즉시 조치. 미커버: 실 브라우저 E2E(.awm 본 worktree 비어있어 ingest 실데이터 없음, `awm serve` 미기동) + dl grid 위 dt-dd a11y 실 AT 확인. **한계**: 실 ingest 세션의 상세 시안 매핑(대화 맥락·명령·매칭 commit)은 S1.6 baseline #3 어댑터 25→7 확장과 동일 항목으로 *S2 측정 후 데이터 기반* 우선순위 결정. 산출물: `docs/verifications/m0-s1.7-session-detail-id-mismatch.md`. **NOVA-STATE Blocker #2 closed**. 신규 Blocker #5: M0/S2 측정 환경(serve 꺼짐 + capture hook 본 worktree 미설치) — *사용자 작업 위치* 정보가 필요한 운영 결정. | 2026-05-13

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
> **AI Audit Trail SaaS for Korean SMB.**
44
> AI 에이전트가 자율적으로 만드는 변경(코드·DB·인프라)을 팀이 검토·감사·복원할 수 있게 하는 한국 B2B SaaS.
55
6+
## Quick Start — 테스터 (5분)
7+
8+
```bash
9+
git clone https://github.com/TeamSPWK/agent-work-memory.git
10+
cd agent-work-memory
11+
npm run init # Node ≥ 20 · install · build · Claude Code hook 자동 머지 · serve
12+
```
13+
14+
이후 Claude Code를 *완전 종료 후 재시작*http://127.0.0.1:5173/today
15+
16+
브라우저로 단계별 가이드: [`docs/tester-quickstart.html`](./docs/tester-quickstart.html) (self-contained, file:// 로 바로 열기)
17+
618
## 현재 단계 — Design-First Restart (2026-05-10~)
719

820
본 레포는 **디자인 우선 단계**입니다. v1 구현(2025-09 ~ 2026-05) 8개월치 코드는 본 시점에 의도적으로 archive하고, *PRD 가치를 시각으로 먼저 검증한 뒤 새로 구현*하는 방향으로 전환했습니다.

bin/awm.mjs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ async function main() {
109109
}
110110

111111
if (scope === "capture" && action === "install" && target === "claude") {
112-
installClaudeHook();
112+
const autoMerge = args.includes("--auto-merge");
113+
installClaudeHook({ autoMerge });
113114
return;
114115
}
115116

@@ -401,7 +402,7 @@ function detectRisk(searchable) {
401402
return undefined;
402403
}
403404

404-
function installClaudeHook() {
405+
function installClaudeHook({ autoMerge = false } = {}) {
405406
ensureState();
406407
const hooksDir = join(stateDir, "hooks");
407408
mkdirSync(hooksDir, { recursive: true });
@@ -435,7 +436,56 @@ spawnSync(process.execPath, [${JSON.stringify(cliPath)}, "capture", "event", "--
435436
writeFileSync(snippetPath, `${JSON.stringify(snippet, null, 2)}\n`);
436437
console.log(`Claude hook script written: ${hookPath}`);
437438
console.log(`Claude settings snippet written: ${snippetPath}`);
438-
console.log("Merge the snippet into .claude/settings.json after review.");
439+
440+
if (autoMerge) {
441+
const result = autoMergeClaudeSettings(snippet);
442+
console.log(`Auto-merge: ${result.action} ${result.path}`);
443+
if (result.added > 0) console.log(` + ${result.added} hook group(s) appended.`);
444+
if (result.skipped > 0) console.log(` · ${result.skipped} already present (idempotent).`);
445+
console.log("Claude Code 재시작 후 새 세션부터 캡처가 시작됩니다.");
446+
} else {
447+
console.log("Merge the snippet into .claude/settings.local.json (or pass --auto-merge).");
448+
}
449+
}
450+
451+
function autoMergeClaudeSettings(snippet) {
452+
const claudeDir = join(cwd, ".claude");
453+
const settingsPath = join(claudeDir, "settings.local.json");
454+
mkdirSync(claudeDir, { recursive: true });
455+
456+
let existing = {};
457+
let action = "created";
458+
if (existsSync(settingsPath)) {
459+
try {
460+
existing = JSON.parse(readFileSync(settingsPath, "utf8"));
461+
action = "merged into";
462+
} catch (error) {
463+
throw new Error(`.claude/settings.local.json corrupt — please fix manually: ${error.message}`);
464+
}
465+
}
466+
467+
existing.hooks = existing.hooks ?? {};
468+
let added = 0;
469+
let skipped = 0;
470+
for (const [eventName, hookGroups] of Object.entries(snippet.hooks)) {
471+
existing.hooks[eventName] = existing.hooks[eventName] ?? [];
472+
for (const group of hookGroups) {
473+
const groupCommands = group.hooks?.map((h) => h.command) ?? [];
474+
const isDuplicate = existing.hooks[eventName].some(
475+
(g) =>
476+
(g.matcher ?? null) === (group.matcher ?? null) &&
477+
(g.hooks?.map((h) => h.command) ?? []).join("|") === groupCommands.join("|"),
478+
);
479+
if (isDuplicate) {
480+
skipped += 1;
481+
} else {
482+
existing.hooks[eventName].push(group);
483+
added += 1;
484+
}
485+
}
486+
}
487+
writeFileSync(settingsPath, `${JSON.stringify(existing, null, 2)}\n`);
488+
return { path: settingsPath, action, added, skipped };
439489
}
440490

441491
function installGitHook() {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Tester Onboarding — One-Command Init + HTML Quickstart
2+
3+
> 2026-05-13. PRD §11 H4(5분 first-value)를 받치는 외부 테스터 onboarding 압축.
4+
5+
## Context
6+
7+
현재 onboarding 8 단계(clone → install → build → capture install → cp snippet → Claude 재시작 → serve → 브라우저)는 *1인 ad-hoc*이라 외부 테스터에 비현실적. *Claude Code 재시작 1회*만 우회 불가, 나머지 7단계는 1 명령으로 압축 가능.
8+
9+
## Problem (MECE)
10+
11+
1. **다단계 명령** — 한 줄 실수로 정지
12+
2. **수동 머지**`cp snippet → settings.local.json` 사용자 직접
13+
3. **사전 점검 부재** — Node 버전·`.claude/` 디렉토리 권한 등 체크 없음
14+
4. **가이드 없음** — README는 1인 운영자 톤, 테스터 입장 onboarding 문서 X
15+
5. **재시작 안내 모호**** 재시작 필요한지 설명 부재
16+
17+
## Solution
18+
19+
### S1. `bin/awm.mjs``capture install claude --auto-merge`
20+
21+
기존 install 동작은 그대로(snippet 생성), `--auto-merge` 플래그 추가 시:
22+
- `.claude/` 없으면 mkdir
23+
- `.claude/settings.local.json` 없으면 snippet 그대로 복사
24+
- 있으면 *hooks 깊이 머지* (eventName별 그룹 append, 동일 command + matcher dedup)
25+
- 결과 경로 + 변경 요약 출력
26+
27+
### S2. `scripts/awm-init.sh` — orchestrator
28+
29+
```
30+
#!/usr/bin/env bash
31+
1) Node 버전 ≥ 20 체크 (vite8 요구)
32+
2) npm install (idempotent, --silent)
33+
3) npm run build (idempotent — dist 있으면 skip)
34+
4) node bin/awm.mjs capture install claude --auto-merge
35+
5) bash scripts/awm-serve.sh start
36+
6) 안내 출력 — Claude Code 재시작 + http://127.0.0.1:5173/today
37+
```
38+
39+
오류 시 명확한 메시지 + 다음 단계 안내.
40+
41+
### S3. `package.json npm run init`
42+
43+
```json
44+
"init": "bash scripts/awm-init.sh"
45+
```
46+
47+
### S4. `docs/tester-quickstart.html` — 단일 self-contained HTML
48+
49+
- inline CSS, 시스템 폰트, prefers-color-scheme 다크 자동
50+
- Toss-style 외부 페이지 톤 (`.pub-*` 정신)
51+
- 구조: hero (5분 안에) → 1단계 init → 2단계 Claude 재시작 → 3단계 첫 세션 → FAQ 4건 → 트러블슈팅 5건
52+
- *file://* 로 바로 열기 가능 (테스터가 clone 직후 브라우저로 더블클릭)
53+
- 외부 노출 금지 7항(§12.3) 준수 — 내부 가설명·mock 숫자 없음
54+
55+
### S5. CLAUDE.md "Build & Verify" 갱신
56+
57+
*테스터 onboarding* 한 줄로 압축 (`npm run init`) + tester-quickstart.html 링크.
58+
59+
### S6. README 한 줄
60+
61+
"Quick Start (5분)" 섹션에 `docs/tester-quickstart.html` 링크. (README 최소 변경)
62+
63+
## Verification
64+
65+
- 빈 상태에서 시뮬레이션 — `.claude/settings.local.json`·`dist/`·`.awm/` 삭제 후 `npm run init` 한 번 → 모든 단계 PASS 출력 + serve 응답 ok
66+
- `--auto-merge` 중복 호출 idempotent — 두 번째 호출에서 hooks 중복 추가 0
67+
- 빌드 + tsc + web 67/67 + node 40/40 회귀
68+
- HTML quickstart — Chrome/Safari file:// 정상 렌더
69+
- Evaluator 독립 서브에이전트
70+
71+
## 영향 파일 (5 신규 + 2 수정)
72+
73+
| 파일 | 변경 | 라인 |
74+
|------|------|------|
75+
| `bin/awm.mjs` | --auto-merge 옵션 + autoMergeClaudeSettings 함수 | +60 |
76+
| `scripts/awm-init.sh` | 신규 | ~80 |
77+
| `package.json` | npm run init 추가 | +1 |
78+
| `docs/tester-quickstart.html` | 신규 self-contained | ~250 |
79+
| `CLAUDE.md` | Build & Verify 압축 + quickstart 링크 | ±10 |
80+
| `README.md` | Quick Start 한 줄 (위치 결정) | +3 |
81+
| `docs/projects/plans/tester-onboarding.md` | 본 plan ||
82+
83+
## Exit Criteria
84+
85+
- [ ] 빈 worktree에서 `npm run init` 한 줄로 끝 (Node check → install → build → hook merge → serve start)
86+
- [ ] `--auto-merge` 두 번째 호출 idempotent (hooks 중복 0)
87+
- [ ] HTML quickstart file:// 정상 렌더 + 외부 노출 금지 7항 통과
88+
- [ ] 빌드/테스트 회귀 0
89+
- [ ] Evaluator PASS

0 commit comments

Comments
 (0)