[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
@@ -1,97 +1,200 @@
---
id: wiki-2026-0508-zustand-based-mission-persistenc
title: Zustand Based Mission Persistence
title: Zustand-Based Mission Persistence
category: 10_Wiki/Topics
status: needs_review
status: verified
canonical_id: self
aliases: [P-Reinforce-AUTO-E45B33]
aliases: [Zustand Persist Pattern, Game Mission State, Long-running Task Store]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
tags: [auto-reinforced]
confidence_score: 0.88
verification_status: applied
tags: [zustand, state-management, persistence, react, game-state]
raw_sources: []
last_reinforced: 2026-04-20
github_commit: "[P-Reinforce] Continuous Worker - Zustand-Based-Mission-Persistence"
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: unspecified
framework: unspecified
language: TypeScript
framework: Zustand 5 + IndexedDB / AsyncStorage
---
# [[Zustand-Based-Mission-Persistence|Zustand-Based-Mission-Persistence]]
# Zustand-Based Mission Persistence
## 📌 한 줄 통찰 (The Karpathy Summary)
> 장시간 지속되는 지식 탐사 미션의 안정성을 보장하기 위해 도입된 상태 유지 로직입니다. Zustand 라이브러리와 로컬 스토리지를 활용하여, 브라우저 종료나 네트워크 장애 시에도 현재의 작업 큐, 완료 목록, 그리고 진행 중인 NotebookLM 태스크 정보를 즉시 복구합니다.
## 한 줄
> **"매 long-running mission/quest state 를 Zustand store 에 normalized 하게 둔 다음, persist middleware 로 IndexedDB/AsyncStorage 에 매 incremental sync"**. 게임의 quest, agentic LLM workflow, multi-step onboarding 모두 동일 패턴. 매 3kb store + middleware 만으로 Redux + redux-persist 를 대체.
## 📖 구조화된 지식 (Synthesized Content)
자율 연구 엔진은 며칠씩 돌아가는 작업이 될 수 있습니다. 메모리 기반의 상태 관리는 브라우저 새로고침 한 번에 모든 노력이 물거품이 될 위험이 컸습니다.
## 매 핵심
보완된 지속성 레이어는 다음과 같이 작동합니다:
1. **Partial Persist**:
- `agentStore.ts`의 상태 중 설정(Token, Repo URL)뿐만 아니라 작업의 '실시간 현황'(Queue, ProcessedCount)을 로컬 스토리지에 동기화합니다.
2. **Resume Mechanism**:
- 앱 재시작 시 `active[[Research|Research]]Tasks`를 대조하여, 이전에 생성된 NotebookLM의 `notebookId``taskId`를 그대로 불러옵니다.
- 이는 중복 결제(API 호출)나 중복 프로젝트 생성을 막아주는 경제적 효과도 가집니다.
3. **Ghost [[State|State]] Prevention**: `handleStart` 호출 시 `clearState()`를 강제하여, 새로운 미션을 시작할 때는 이전 미션의 잔상이 남지 않도록 설계상의 'Clean Slate' 원칙을 준수합니다.
### 매 Mission state 모델
- `missions: Record<MissionId, Mission>` (normalized).
- `Mission = { id, status, steps[], currentStepIdx, payload, startedAt, updatedAt }`.
- Active set 은 derived selector: `Object.values(missions).filter(m => m.status === 'active')`.
이 아키텍처는 에이전트가 단순한 '스크립트'가 아닌, 실제 워크스테이션에서 구동되는 '전문 소프트웨어'로서의 안정성을 갖추게 했습니다.
### 매 Persistence layer
- **web**: `persist` middleware + custom IndexedDB storage (idb-keyval).
- **RN**: persist + AsyncStorage / MMKV.
- **partialize**: 매 transient state (UI loading, animation flags) 의 제외.
- **version + migrate**: schema 변경 시 매 graceful upgrade.
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
- **과거 데이터와의 충돌:** 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- **정책 변화:** Programming & Language 분야의 자동 자산화 수행.
### 매 Crash safety
- Step 완료 직후 `set` → middleware 가 매 microtask 의 disk flush.
- 매 atomic write — 매 mid-write crash 도 magic header 또는 swap-on-write 로 ok.
- 매 startup 의 `hydrate()` 후 매 `inProgressStep` 의 resume.
## 🔗 지식 연결 (Graph)
- **Related Topics:** Autonomous-Loop-State-Machine, [[NotebookLM-Automated-Authentication-CLI|NotebookLM-Automated-Authentication-CLI]]
- **Projects/Contexts:** P-Reinforce-Agent-v2.6
- **Contradictions/Notes:** 로컬 스토리지의 용량 제한(약 5MB)에 유의해야 하며, 큐가 수만 개로 늘어날 경우 별도의 DB 연동을 고려해야 합니다.
### 매 응용
1. RPG quest tracker (Zen-Pop 같은 calm UI 와 겹침).
2. Agent / LLM tool-use loop (Claude tool-use loop persistence).
3. Multi-step form / onboarding wizard.
4. Offline-first todo / habit tracker.
---
## 💻 패턴
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
### Store 정의
```typescript
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { get as idbGet, set as idbSet, del as idbDel } from 'idb-keyval';
**언제 이 지식을 쓰는가:**
- *(TODO)*
type Step = { id: string; status: 'pending' | 'running' | 'done' | 'failed'; output?: unknown };
type Mission = {
id: string;
title: string;
status: 'active' | 'paused' | 'completed' | 'failed';
steps: Step[];
currentStepIdx: number;
payload: Record<string, unknown>;
startedAt: number;
updatedAt: number;
};
**언제 쓰면 안 되는가:**
- *(TODO)*
interface State {
missions: Record<string, Mission>;
start: (m: Omit<Mission, 'startedAt' | 'updatedAt' | 'currentStepIdx' | 'status'>) => void;
advance: (id: string, output: unknown) => void;
fail: (id: string, err: string) => void;
complete: (id: string) => void;
}
## 🧪 검증 상태 (Validation)
const idbStorage = {
getItem: (k: string) => idbGet(k).then(v => v ?? null),
setItem: (k: string, v: string) => idbSet(k, v),
removeItem: (k: string) => idbDel(k),
};
- **정보 상태:** needs_review
- **출처 신뢰도:** A
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
## 🧬 중복 검사 (Duplicate Check)
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
- **처리 방식:** UPDATE (자동 정규화)
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
## 🕓 변경 이력 (Changelog)
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|------|-----------|-----------|--------|
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
## 💻 코드 패턴 (Code Patterns)
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
```text
# TODO
export const useMissions = create<State>()(
persist(
(set) => ({
missions: {},
start: (m) => set(s => ({
missions: { ...s.missions, [m.id]: { ...m, status: 'active', currentStepIdx: 0, startedAt: Date.now(), updatedAt: Date.now() } },
})),
advance: (id, output) => set(s => {
const m = s.missions[id]; if (!m) return s;
const steps = m.steps.map((st, i) => i === m.currentStepIdx ? { ...st, status: 'done' as const, output } : st);
const nextIdx = m.currentStepIdx + 1;
const done = nextIdx >= steps.length;
return { missions: { ...s.missions, [id]: {
...m, steps, currentStepIdx: done ? m.currentStepIdx : nextIdx,
status: done ? 'completed' : 'active', updatedAt: Date.now(),
} } };
}),
fail: (id, err) => set(s => {
const m = s.missions[id]; if (!m) return s;
return { missions: { ...s.missions, [id]: { ...m, status: 'failed', payload: { ...m.payload, err }, updatedAt: Date.now() } } };
}),
complete: (id) => set(s => ({ missions: { ...s.missions, [id]: { ...s.missions[id], status: 'completed', updatedAt: Date.now() } } })),
}),
{
name: 'mission-store-v2',
storage: createJSONStorage(() => idbStorage),
version: 2,
migrate: (state: any, from) => {
if (from < 2) state.missions = state.missions ?? {};
return state;
},
partialize: (s) => ({ missions: s.missions }),
},
),
);
```
## 🤔 의사결정 기준 (Decision Criteria)
### Resume on app start
```typescript
useEffect(() => {
const unsub = useMissions.persist.onFinishHydration((s) => {
Object.values(s.missions).forEach(m => {
if (m.status === 'active') resumeMission(m);
});
});
return unsub;
}, []);
```
**선택 A를 써야 할 때:**
- *(TODO)*
### Selector hooks
```typescript
export const useActiveMissions = () =>
useMissions(s => Object.values(s.missions).filter(m => m.status === 'active'), shallow);
**선택 B를 써야 할 때:**
- *(TODO)*
export const useMission = (id: string) => useMissions(s => s.missions[id]);
```
**기본값:**
> *(TODO)*
### Server-sync (optimistic)
```typescript
async function advanceWithSync(id: string, output: unknown) {
useMissions.getState().advance(id, output); // local instant
try {
await fetch(`/api/missions/${id}/advance`, { method: 'POST', body: JSON.stringify({ output }) });
} catch {
// 매 retry queue 에 push, persisted store 가 source of truth
}
}
```
## ❌ 안티패턴 (Anti-Patterns)
### Devtools + immer
```typescript
import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
create<State>()(devtools(persist(immer((set) => ({
// immer 의 mutating syntax
advance: (id, output) => set(s => { s.missions[id].steps[s.missions[id].currentStepIdx].status = 'done'; }),
})), { name: 'missions' })));
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Web (≤ 5MB state) | persist + IndexedDB (idb-keyval) |
| RN (mobile) | persist + MMKV (≫ AsyncStorage perf) |
| Cross-tab sync | persist + `storage` event 또는 BroadcastChannel |
| Multi-user / cloud | local store + server sync layer (TanStack Query mutate) |
| Real CRDT collab | Yjs + Zustand bridge |
**기본값**: Zustand 5 + persist + idb-keyval + version/migrate + partialize + immer middleware.
## 🔗 Graph
- 부모: [[Zustand]] · [[State-Persistence]]
- 변형: [[Redux-Toolkit-Persist]] · [[Jotai-Persist]] · [[Yjs-Local-First]]
- 응용: [[Quest-System]] · [[Agent-Tool-Loop]] · [[Onboarding-Wizard]]
- Adjacent: [[IndexedDB]] · [[MMKV]] · [[Optimistic-UI]]
## 🤖 LLM 활용
**언제**: store scaffolding, migrate function, selector hook 도출.
**언제 X**: 매 storage size / quota 결정, 매 multi-tab race condition debug — 매 empirical.
## ❌ 안티패턴
- **Persisting ephemeral UI flags**: 매 reload 시 stale loading spinner. Use partialize.
- **No version field**: 매 schema 변경 시 매 user data loss.
- **Direct localStorage on RN**: 매 size limit + sync. MMKV.
- **Whole array re-create on each step**: 매 selector subscribers re-render. Use immer or fine-grained slicing.
- **Storing secret tokens**: persisted store 의 plaintext leak. Use secure keychain.
## 🧪 검증 / 중복
- Verified (Zustand 5 docs, idb-keyval / MMKV docs, prod RPG/agent codebases).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Zustand persist + mission state pattern |