얇은 현재상태 포인터. 완료 이슈의 묘지가 아니다. 현재 방향·활성 이슈·최근 proof·다음 행동만. 슬라이스 별 상세는
memory/workcell-project.md에 압축 보존.
- PLAN §9 전부 ✅ + ultracode 하드닝 + Phase 2(P2 §3 #3 실 LLM pair invoker + #4/D12 outbound MCP 브리지) 전체 구현·재검증 완료. 아래 ‘Phase 2 진행’ 섹션 참조(WC-57~68, 재검증
wnodxav4hPASS). MVP-외 잔여(명시 disclosed): codex MCP 주입, Workcell-MCP 자동주입, remote MCP relay, 실 OD 데몬 연동, image pixel diff. - PLAN §9 하드닝 이력: 모든 11개 수용 기준 ✅(WC-23
49). 이후 ultracode 멀티에이전트 검증 워크플로(10 에이전트)로 교차-슬라이스 회귀 + 보안 클러스터 적발 → **WC-5055 하드닝 배치**로 전부 수정:- WC-50 WC-13 teardown 교차회귀 수정(WC-29 트리거가 activity_log DELETE 막음 → TRUNCATE).
- WC-51 capability 보안 클러스터 — assign/register/PATCH board-only(agent self-grant/우회/위조 차단).
- WC-52 pair orchestrator — cap pre-check(extra billed turn 제거), convergence-at-boundary, stopPolicy.maxRounds/abortOn 적용.
- WC-53 capability scope uniqueness NULLS NOT DISTINCT(migration 0097) + revoked 재활성 assign.
- WC-54 knowledge-graph — parent lookup tenant-scope, ON CONFLICT 원자 idempotency, backfill 서비스 재인스턴스 제거.
- WC-55 dispatch queued-run + per-agent 제외, design-artifacts previewUrl/body 반환(#4 데이터 overclaim 수정).
- 재검증 워크플로(
wslzdl135, 7 에이전트): 150/150 테스트 green(서버 78 + UI 63 + e2e 9), 16개 finding 전부 CLOSED 확인 — 단, WC-54가 도입한 새 MEDIUM 회귀 1개 적발 → WC-56으로 수정:- WC-56 knowledge-graph parent 노드 metadata clobber 수정. upsertNode가 이제 metadata 제공 시 항상 ON CONFLICT update → syncIssueAsNode의 sparse
{issueId}parent upsert가 기존 parent의 status/priority/workMode를 덮어씀(backfill 순서 의존).buildIssueNodeMetadata()헬퍼로 child·parent 동일 full metadata 생산. wc28 12/12. - 교훈: per-slice hermetic green ≠ 통합 green. 배치 후 full 회귀(ultracode 워크플로) 필수 — 그리고 fix가 도입한 새 회귀까지 재검증으로 포착.
- WC-56 knowledge-graph parent 노드 metadata clobber 수정. upsertNode가 이제 metadata 제공 시 항상 ON CONFLICT update → syncIssueAsNode의 sparse
- **이해 워크플로(
wg43z96gc, 5 에이전트)**로 두 기능 substrate/net-new 매핑 → 11-슬라이스 계획(LANE A pair-invoker, LANE B mcp-bridge; 임계경로 WC-60→61→64→66). - LANE A = 실 LLM pair invoker (#3) 완성:
- WC-57 공유 cost-mapping util(heartbeat에서 verbatim 추출) +
runAdapterSingleTurn헬퍼(heartbeat hot path 미변경). - WC-58
buildRealPairTurnInvoke— adapter.execute 1턴 → PairTurnInvokeResult 매핑(summary/outcome/cost), 실패/timeout→abort graceful,WORKCELL_PAIR_LIVE_LLM플래그 게이트(stub 기본/CI hermetic). - WC-59 issue grounding — buildPairTurnExecutor가 issues.pairGroupId 역참조(tenant-scoped)로 issue 컨텍스트 주입.
- WC-57 공유 cost-mapping util(heartbeat에서 verbatim 추출) +
- LANE B 완료(8/8):
- WC-60
@workcell/mcp-bridge새 패키지 — outbound MCP 클라이언트(SDK StdioClientTransport), McpClientError sanitized(서버 경로 미노출). hermetic 테스트=in-repo fake stdio MCP 서버. 유일 net-new 인프라. - WC-62 KG inbound MCP 툴 3개(mcp-server: graphNodes/Neighborhood/SyncIssues → 기존 KG REST). graphUpsert(임의 write)는 라우트+authz 필요해 follow-up.
- WC-61
mcpClientRegistry(server) — capability-gated(active company-wide assignment 필수) per-(company,key) 싱글톤+telemetry. SECURITY: 매 호출 재게이팅(revocation 즉시 반영), 미인가/미설정→spawn 안 함. fake-client seam hermetic 9/9. - WC-63 KG outbound enrichment(첫 registry 소비자) —
?enriched=true면 'graph-enrichment' MCP 호출해 pointer overlay 병합, 미설정/에러/timeout→base graph(graceful). app.ts가 app-scoped registry 1개 생성·주입. wc28 17/17. - WC-64 MCP 서버 capability bootstrap —
registerMcpServers(db, companyId)가 graph-enrichment(D12)+open-design(D13)을 'mcp' capability로 시드, 서버 command는 env 설정(WORKCELL_*_MCP_COMMAND); 설정됨→active, 미설정(dev)→pending(registry 거부). companyService.create()에 best-effort 훅. 라우트: mcp capability에 command 없으면 400. 교차-슬라이스 cascade 수정(머지게이트 회귀가 적발): capabilities/capability_assignments company_id FK에 onDelete cascade 누락 → create()가 capabilities 시드하자 companies-service teardown FK 위반 → migration 0098로 cascade 추가(데이터 모델 정합). wc64 4/4 + 회귀 35/35. - WC-65 plugin
ctx.mcpClientshost↔worker RPC(가장 복잡) — 새mcp.clientcapability + protocol mcp.callTool/listTools + worker ctx 프록시 + host-client-factory gated 핸들러 + buildHostServices가 registry 백킹(dispose 정리). SECURITY: host가 companyId를 신뢰된 invocation scope에서 도출(plugin params 아님)→cross-tenant 차단; scope 없으면 거부. SDK 5/5(+전체 20/20). - WC-66 OD plugin이 ctx.mcpClients 소비 —
refreshOpenDesignArtifacts가 open-design MCP getArtifacts→issue-scoped state 캐시, 미설정/에러/throw→graceful 폴백(table route). manifest +mcp.client/state. worker.spec 4/4(플러그인에 vitest 신설).
- WC-60
- **검증 워크플로(
wh2k9z419)**로 WC-57~62 적대적 재검증 → 4/5 hold + 2 결함 → WC-68 수정: (1) WC-57 cost-map verbatim 깨짐(untrimmed↔trimmed," subscription "under-bill) → parity 복원+가드. (2) WC-60 connect timeout flaky → connectTimeoutMs 분리. wc57 5/5·mcp-bridge 7/7.- WC-67 adapter MCP 주입(claude-local, .mcp.json) —
adapter-utils/mcp-servers.ts(extract/buildMcpJsonConfig/buildWorkcellMcpServerSpec/flag, pure browser-safe) + heartbeat가 agent.adapterConfig.mcpServers→context.workcellMcpServers(presence-gated) + claude-local이WORKCELL_ADAPTER_MCP_INJECTION플래그 ON+local+서버 존재 시 prompt bundle dir(repo cwd 아님→clobber 없음)에 .mcp.json 작성+--mcp-config. OFF 기본=프로덕션 run-path byte-단위 불변. codex/Workcell-자동주입/remote relay=문서화 follow-up. adapter-utils 7/7.
- WC-67 adapter MCP 주입(claude-local, .mcp.json) —
- LANE B 8/8 완료 → Phase 2 (#3 실 LLM pair invoker + #4/D12 outbound MCP 브리지) 전체 구현 완료 + ultracode 재검증 PASS.
- 재검증 워크플로(
wnodxav4h, 7 에이전트): 병렬 테스트 70/70 green(adapter-utils 7·sdk 20·server KG+registry+bootstrap 30·OD 4·e2e 9) + 적대적 리뷰 WC-63~67 5/5 hold, 새 결함 0개. 핵심 확인: WC-65 테넌트 스코핑(worker가 companyId 위조/author 불가 — invocation scope는 host 소유 opaque UUID 매핑)·WC-67 기본경로 byte-단위 불변(플래그 OFF)·migration 0098 cascade tenant-scoped(over-delete 없음)·WC-63 graceful/pointer-only. wh2k9z419(WC-68 1건)와 달리 완전 clean — fix 슬라이스 불요.- WC-69 codex-local MCP 주입(WC-67 claude의 codex 대칭 — adapter 커버리지 완성):
buildCodexMcpToml(adapter-utils, quoted[mcp_servers."name"]TOML) + codex-local이 flag ON+local+서버 존재 시 managed CODEX_HOME/config.toml에 append(dup-name 가드). flag OFF=불변. adapter-utils 9/9.
- WC-69 codex-local MCP 주입(WC-67 claude의 codex 대칭 — adapter 커버리지 완성):
- proof: WC-57~69 각 슬라이스 green·WC-68 green · 전체 워크스페이스 typecheck 19/19(db migration 0098, UI browser-safe) · e2e 5회 통과(9/9). 각 슬라이스
--no-ff머지. adapter MCP 주입 = claude+codex 양쪽 완성. - 남은 follow-up(자율 완결 불가 — 결정/외부시스템 필요): ⓐ Workcell-MCP 자동주입(buildWorkcellMcpServerSpec 빌드·테스트 완료, but 신뢰 apiUrl을 heartbeat hot-path에 threading 필요 — 배포설정 의존, flag-OFF 실험이라 보류) ⓑ remote MCP relay(SSH stdio relay 인프라 — 설계 필요) ⓒ 실 Open Design 데몬 연동(데몬 부재) ⓓ image pixel diff(OD raster diff — 별도 UI 기능) ⓔ GitHub push 전 squash(원격 미존재 + destructive 히스토리 재작성 → org/repo/공개여부 결정 필요).
- WC-70 de-Paperclip 브랜드: 순수 무채색(chroma 0)=generic 포크 느낌 → Orchestration/보드 방향 + 브랜드 인디고-블루 accent(
--primary/--ring/--sidebar-primary만, surface·status 불변) + WorkcellMark(orchestration hub: 중앙 오케스트레이터 노드+연결 에이전트 노드들, 클립 마크 대체) 사이드바 상단 + /design-guide Brand 섹션. - WC-71 철학 align + 다크 기본 (사용자 피드백 반영): 사용자 지적 4건 — (1) 기본 Agent 이름 placeholder "CEO", (2) 모델 목록에 최신 모델(Opus 4.8·codex 5.5) 부재, (3) 온보딩 first-plan이 "You are the CEO…hire a founding engineer" 레거시, (4) 흰 배경 눈부심.
- 레거시 CEO/회사창업 메타포 → Orchestrator 정체성. PLAN_ANCHOR 정합(사람=board가 방향·승인 소유, 에이전트=기능역할 실행).
ceoenum 키는 인증/루트 프리미티브(authorization.ts superuser·canCreateAgents·회사 mutation 게이트·org-chart root)라 보존(마이그레이션 위험 회피) — 대신 사용자 가시 정체성만 리프레임:AGENT_ROLE_LABELS.ceo"CEO"→**"Orchestrator"**; 온보딩 기본 이름/placeholder "CEO"→"Orchestrator"; first-task 카피(보드→오케스트레이터 방향: 이슈+acceptance criteria+proof surface); 페르소나 3종(SOUL/AGENTS/HEARTBEAT.md) 전면 재작성(P&L·hire/fire·부서 C-suite 제거 → board가 판단 소유·proof 규율·1이슈 1오너·기능역할 라우팅);approve_ceo_strategy라벨 "Strategy"; seed.ts "Orchestrator". - 모델 = 이미 지원됨(질문 답변+UI 명확화): "Default"(빈 값)=adapter가
--model생략→로컬 claude/codex가 자기 기본 모델 사용(claude-local/codex-local 양쪽 execute.ts 확인); 박스는 creatable(임의 모델 직접 입력 가능). ModelDropdown "Default" 버튼에 "Uses your local CLI's own model" 부제 추가. 정적 모델 목록은 stale 가능 → Default/직접입력 권장. - 다크 기본(차분한 차콜 그레이). index.html 기본 light→dark + 스토리지 키
workcell.theme→workcell.color-theme(WC-70 light 기본이 ThemeProvider mount 시 자동영속돼 세션이 light에 핀 고정되는 것 방지)..dark무채색을 near-black(bg 0.145)→차콜(bg 0.205·card/popover 0.248·secondary/muted/accent 0.285·border/input 0.30·sidebar 0.17) 상향=눈부심↓·elevation 명확. 브랜드/상태/차트색 불변. design-guide 스킬 다시 dark-default로 갱신. - proof: 페르소나 번들 단위테스트 15/15(SOUL/AGENTS/HEARTBEAT "Orchestrator" 단언)·UI 라벨 테스트 21/21(OrgChart·KanbanBoard)·typecheck(진행). 동반 수정: e2e 3종(placeholder="CEO"→"Orchestrator", onboarding desc 단언) + agent-skills-routes 페르소나 단언.
- 레거시 CEO/회사창업 메타포 → Orchestrator 정체성. PLAN_ANCHOR 정합(사람=board가 방향·승인 소유, 에이전트=기능역할 실행).
- WC-72 C-suite 역할 → 기능형 taxonomy (사용자: "ceo/cto/cmo/cfo 구분이 우리 scope를 좁힌다 → 기능형 이름으로 지금 바꿔라"): AskUserQuestion으로 세트 확정.
AGENT_ROLES=orchestrator·lead·pm·engineer·designer·researcher·writer·qa·security·devops·general(ceo→orchestrator, cto→lead, cmo/cfo 제거, writer 추가).ceo가 인증 루트라 전 인증표면 원자적 리네임: 모든role==="ceo"→"orchestrator"(authorization superuser·canCreateAgents·회사 mutation·planner-capable·link-approval·branding/portability/openclaw·workspace-runtime authz), 에스컬레이션["cto","ceo"]→["lead","orchestrator"](recovery+productivity). 페르소나 폴더onboarding-assets/ceo→orchestrator. org-chart SVG 태그·guessRoleTag·schemaRoles. UI 6파일 +AgentAccessState.taskAssignSource타입ceo_role→orchestrator_role. migration 0099(role=text → 기존 행 UPDATE ceo→orchestrator/cto→lead/cmo·cfo→general; enum ALTER 불요). portability 라벨맵=additive(기능형+레거시 C-suite 라벨 유지 → 외부 임포트 회사 충실 렌더). 문서(서브에이전트)·스킬 예시·테스트(서브에이전트 14파일+메시지 단언 6+storybook) 동반. proof: 전체 typecheck 클린(전 패키지)+역할/인증 10 server 스위트 152/152. 잔여 fulltest:runred는 WC-72 무관 검증완료: 미수정 issue-route 테스트의 사전존재 service-mock 갭(compoundFollowupService, 역할 무관), Windowsspawn sh ENOENT(workspace-runtime), wc28/wc61=병렬 contention(단독 실행 green). 55파일--no-ff머지(7360f11). 누적 73 슬라이스. - WC-73 머지게이트 복구 (red suite cleanup): 미수정 issue-route 테스트들이
../services/index.js목에서compoundFollowupService(+done-transition 경로의issueRequiresProofForDone)를 빠뜨려issueRoutes마운트 시 throw → 사전존재 red 블록이 게이트를 가렸음(WC-72 검증을 가릴 뻔). issueRoutes 마운트하는 테스트 파일에 누락 export 추가(22개 비-issueRoutes 파일은 미변경).*.loggitignore. proof: 영향 12스위트 86/86(순차--no-file-parallelism)·typecheck 클린.⚠️ 교훈: DB 백엔드 스위트는 고병렬 시 Windows 리소스 contention으로 거짓 red → 순차/stable 러너가 신뢰 신호. 머지cb144a1. - WC-74 잔여 Paperclip 정리: 런타임 UI의 오해 소지 alias
Paperclip as Workcell(첨부 아이콘이 제품명으로 alias됨) →AttachmentIcon(5파일/7사용처 + CompanySkillsicon:값). 순수 바인딩 리네임(동일 아이콘 렌더). proof: alias 잔재 0·UI typecheck 클린. 머지543b5fe. 누적 75 슬라이스. - WC-75 정체성 마무리 (사용자 "정체성 마무리" 선택): ⓐ CLI
bootstrap-ceo→bootstrap-admin(휴먼 instance-admin 부트스트랩; 파일/함수/index·run·onboard 호출부 + CloudAccessGate UI 힌트 + docker-smoke + DOCKER.md).bootstrap_ceoinviteType DB 값 +pcp_bootstrap_토큰 prefix는 verbatim 유지(access/health/invite 소비자 계약). 코드 내 hyphen/camelbootstrap-ceo0. ⓑ favicon→WorkcellMark:favicon.svg(브랜드 인디고 #2256BB 라운드사각 + 흰 오케스트레이션 허브) +favicon.ico(16/32/48 PNG-ICO 직접 조립) + 16/32 png + apple-touch + PWAandroid-chrome-192/512(웹 manifest 참조) 재생성. 재사용scripts/generate-favicon.mjs(sharp via server dep). proof: 전체 typecheck 클린·ICO/PNG 매직바이트 검증. 15파일--no-ff머지(4fbb279). 누적 76 슬라이스. → 정체성 arc(WC-70~75) 완료.⚠️ 커밋 위생 사고+수정: 최초git add -A가 무관 6개 issue-route 테스트(+compoundFollowupService)를 WC-75에 흡수 →reset --soft+restore로 6개 되돌리고 15파일로 재커밋(정체성 스코프 유지).
- ✅ red-suite cleanup 완료 (spawned task
fix/issue-route-test-mock-gap): 사용자가 chip 클릭→병렬 세션이 WC-73 미커버 6파일(issue-activity-events·-agent-mutation-ownership·-attachment·-closed-workspace·-comment-cancel·-document-restore)에compoundFollowupService(+issue-activity-events는 done-transitionissueRequiresProofForDone)를 추가(932976f). 검증 후 develop에 통합(f8136b3): 17 issue-route service-mock 스위트 136/136 순차 green + 전체 typecheck 클린. WC-73(12) + 이번(6) = issue-route mock 갭 전부 해소. (※ 내issue/WC-76은 chip 커밋 위에 분기돼 중복 → 삭제; chip 작업을 그대로 통합.) - 잔여 follow-up: ⓐ dev asset-gen 스크립트·역사 doc/plans C-suite 잔재(저가치) ⓑ (원하면) light 톤 soften. substantive 잔여(MCP 자동주입·remote relay·실 OD 데몬·image pixel diff·GitHub push squash)=결정/외부 필요. 그 외 full
test:runred는 Windows-환경(workspace-runtimespawn sh·worktree) — 환경 한정, 코드 무관.
- 사용자 6개 지적(① 모델 Default+직접입력 ② codex 기본모델 강제 ③ pair=2모델 협업 UI 부재·"개발 안한듯" ④ 철학 UI 미노출 ⑤ "Ask the CEO" 레거시 버튼 ⑥ plan 보고 다국어) → Explore 2종으로 정직 재평가(over-claim 안 함) 후 슬라이스화. 6개 전부 해소.
- WC-77 codex 기본모델 강제 1차 수정(codex-local build-config:
model || DEFAULT→ 빈 값이면--model생략=로컬 CLI 자기 기본) + 레거시 "Ask the CEO to create a new agent" 등 CEO 카피→Orchestrator(NewAgentDialog·NewAgent·ReportsToPicker·CompanyExport 역할맵 additive). build-config 3/3. - WC-78 pair(2모델 협업)를 실제 사용·발견 가능 UI 기능으로(#3) —
PairSetupPanel(collapsed CTA→owner/counterpart/maxRounds→pairGroupsApi.create가 workOwnerKind를 pair로 flip) + IssueDetail이workOwnerKind!=="pair"면 셋업 패널/맞으면 라운드 타임라인 렌더 + PairRoundTimeline 행위자 이름 표시. 4/4. - WC-79 모델 필드 = Default + 직접입력 둘만(사용자: 실시간 activated 모델 fetch 불가하면 stale "우르르" 목록 제거)(#1·#2) — AgentConfigForm(main+cheap ModelDropdown: claude/codex면
models=[]+detect off) + OnboardingWizard(non-opencode filteredModels 비움 + "Use this model" 타이핑 affordance + codex 기본강제 2곳 제거 + 미사용 import) + PairSetupPanel.test mock 시그니처(tsc -b) 수정. - WC-80 Workcell 운영철학 UI 직접 노출(#4) —
WorkcellPrinciples(보드=방향·승인 / Orchestrator=회사처럼 실행 / 기능역할 / proof 없으면 Done 아님; Orchestrator AGENTS.md와 동기) → Dashboard 빈상태 + 온보딩 step1 + /design-guide + component-index. 2/2. - WC-81 plan 보고 언어 선호(#6) —
companies.plan_report_language(migration 0100, default 'en') + sharedplan-report-language(16개 큐레이션 code/label/native +resolvePlanReportLanguageLabel=영어/미설정이면 null) +buildPlannerDraftInstruction에 "각 섹션 본문은 <언어>로, 4개 헤딩은 영어 유지(파싱)" 주입 + draft-from-prompt가 회사 언어 thread + 온보딩 step1 셀렉터(브라우저 언어 프리셀렉트). 서버 7/7. (전체 UI i18n 번역=별도 대형 작업, 미착수.) - proof: 각 슬라이스 typecheck 클린(server+ui+shared+db)·타깃 테스트 green(build-config 3·pair 4·principles 2·CompanyContext 9·planner-draft-lang 7)·각
--no-ff머지(WC-7946e4ca3·80ebf5354·81c32d381). 누적 81 슬라이스. - 남은 follow-up: ⓐ plan-report 언어 회사 설정 UI(생성 후 변경; update 스키마는 이미 수용) + portability 반영(export/import에 신규 필드) ⓑ 전체 UI i18n 번역 와이어링(i18next 44 로케일 인프라 존재하나 <2% wired) ⓒ pair "Orchestrator 기본 선호" 확장.
- 진단: i18next 인프라(40 로케일 파일)는 있었으나 0% wired 시드 —
lng:"en"하드락, 스위처 없음,t()쓰는 컴포넌트 1개(App noCompanies 3키)뿐. 각 로케일은 동일 데모 3키만 실제 번역. - WC-82(merge
bdf402e) — 언어 전환 메커니즘 + 계정 메뉴 한국어:locale-preference.ts(저장값>브라우저 exact→base>영어 감지·localStorage 영속·<html lang/dir>RTL ar/fa/he/ur·Intl.DisplayNamesendonym=40개 네임맵·신규 deps 불요) +index.ts가lng를 detectInitialLocale로 init(하드코드 en 탈피)+setLocale/getCurrentLocale+LanguageSelect(계정 메뉴; 지원 로케일 native명 나열, 즉시 전환=react-i18next 재렌더) +locale-validation.ts완화: 부분 로케일 허용(누락 키=런타임 영어 폴백 → 키마다 40파일 안 건드리고 점진 번역; extra/injection/placeholder/length 검사 유지) + SidebarAccountMenu 라벨 t() + ko.json. 테스트 15(preference 7·LanguageSelect 1·validation 7). - WC-83(merge
38ed66a) — 사이드바 내비 번역(항상 보이는 표면): Sidebar.tsx 전체 내비(New Issue·검색·Dashboard·Inbox·Work/Company 섹션·Issues/Routines/Goals/Workspaces/Org/Skills/Costs/Activity/Settings) + SidebarProjects/Agents 섹션 라벨 →sidebar.*t() + 한국어. 테스트 30(Sidebar/Projects/Agents)+validation 7. - WC-84(
3e03218, develop 직접) — 대시보드 랜딩 번역: 메트릭 카드 4종(라벨+카운트 설명 interpolation)·차트 제목 4+"Last 14 days"·Recent Activity/Tasks 헤딩·no-tasks 빈상태·no-agents 배너·"Open budgets" →dashboard.*t()+한국어(placeholder parity 유지). 드문 budget-incident 배너(영어 복수형)는 의도적 연기. validation 7. - WC-85(
654bd68) — 상태 라벨 공유·번역화(교차): StatusBadge(lowercase)·StatusIcon·KanbanBoard 제각각 → 공유lib/issue-labels.ts(Title Case 영어+번역) +issueStatus.*ko(백로그/할 일/진행 중/검토 중/완료/취소됨/차단됨). 칸반 컬럼·상태칩·picker·행/상세 130 테스트 green(영어 불변). - WC-86 — 우선순위 라벨: PriorityIcon Critical/High/Medium/Low → 공유 priorityLabel +
issuePriority.*ko(긴급/높음/보통/낮음). 58 green. - WC-87 — 이슈 페이지: 플래너 draft 바(placeholder/aria/hint/버튼)+빈상태+breadcrumb →
issues.*ko + 레거시 "ceo role" 카피→orchestrator(#5 잔재). 13 green. - WC-88 — 온보딩 스켈레톤: 탭 4·스텝 헤더/설명 4·footer 버튼(Back/Next/Creating/Launch) →
onboarding.*ko. 필드 라벨 follow-up. validation 7. - WC-89 — plan-report 언어 설정 UI(WC-81 follow-up): CompanySettings General에 언어 셀렉터(생성 후 변경). 2 green.
- WC-90 — plan-report 언어 portability(WC-81 follow-up): manifest type/validator(optional)+export/parse/import 4지점 wiring → 임포트 회사 'en' 리셋 안 됨. 전체 workspace typecheck green·portability 45 green.
- CEO→Orchestrator 카피 스캔 완결: ui/server 사용자노출 "CEO" 잔재 0(CompanyExport ROLE_LABELS=의도적 additive 레거시;
bootstrap_ceo/approve_ceo_strategyDB값 보존). - WC-91 — 이슈리스트 검색바(
issues.search*). WC-92 — 전체앱 i18n 1차(서브에이전트 6병렬, 19페이지 663키): Inbox·MyIssues·Approvals(+Detail)·Agents·Goals(+Detail)·Projects(+Detail)·Routines(+Detail)·Costs·Org(+Chart)·Workspaces·Capabilities·Search·Secrets·NotFound. WC-93 — 2차(7병렬, 27페이지 1483키): IssueDetail·AgentDetail(크롬 중심)·Auth/CliAuth/InviteLanding/BoardClaim·Companies/CompanyAccess/Invites/Environments/Skills/JoinRequestQueue/Export/Import·Plugin/Adapter Manager·Instance(Access/Settings/General/Experimental)/Profile/UserProfile·Workspace/Activity/Cloud Detail. - i18n 서브에이전트 패턴: 각 에이전트가 distinct 파일 t()+defaultValue 배선 + 키를 temp JSON에 Write → 중앙 merge.mjs가 en/ko에 nested 병합(공유 JSON 충돌 회피).
⚠️ 테스트 격리 수정: vitest.setup의 공유 localStorage mock이 locale-switch 테스트 후 i18n을 ko로 남겨 번역 컴포넌트가 뒤집힘 → setup에 매 테스트 전 영어 핀(beforeEach changeLanguage en). placeholder parity·leaf↔parent 충돌(agentDetail.costs→costsHeading) 각 1건 수정. - WC-94 — 컴포넌트 i18n(서브에이전트 6병렬, 27컴포넌트 866키): 다이얼로그(NewIssueDialog·RoutineRunVariablesDialog·ImportFromVaultDialog)·이슈패널(Properties·WorkspaceCard·RelatedWork·Documents)·스레드(Comment·Chat·ThreadInteraction)·이슈 notice 7종·패널(ActiveAgents·WorkspaceRuntime·BlockedInbox·RunLedger·RunInvocation)·nav(CommandPalette·SidebarCompanyMenu·InstanceSidebar·DevRestart·SystemNotice). WC-95 — 설정폼+잔여(3병렬, 250키): OnboardingWizard deep 필드·AgentConfigForm·NewAgentDialog·KanbanBoard·IssueFiltersPopover·MarkdownEditor·FileTree·RoutineHistoryTab(기존 t()/모델드롭다운/plan-report 로직 보존).
⚠️ 수정: placeholder parity(suggestTasks{{noun}})·leaf↔parent 충돌 각 1건. 1개 서브에이전트 모델 세션한도로 메시지 잘림(파일+JSON은 착지 완료, typecheck+스위트로 무결성 확인). - proof: 각 슬라이스 UI/server typecheck 클린·전체 UI 스위트 1114/1114 green(WC-92~95 각각)·머지. 누적 95 슬라이스. plan-report 언어 완성 + 전체앱 i18n 사실상 완료(~46페이지 + ~32컴포넌트 + 전 크롬, ~3400키, 영어 defaultValue 불변·38개 비-ko 언어=영어 폴백).
⚠️ 교훈: i18n init이 ambient locale 의존 → 기본 영어 가정 테스트는 setup에서 영어 핀; 공유 타입 추가 시pnpm typecheck(전 워크스페이스 -r); 대량 i18n=distinct-file 서브에이전트 병렬+키 temp-JSON 자가기록+중앙 merge.mjs 병합. - 정직한 범위: 메커니즘 완성 + 한국어 검증 표면 2개(계정메뉴·사이드바). 전체 40개 언어 번역은 별도 파이프라인 — 검증 불가한 38개 언어 기계번역은 안 함(부분 로케일+영어 폴백으로 점진 확장). follow-up: 온보딩·대시보드·이슈/칸반·공통 버튼 순차 번역, 원하면 언어별 서브에이전트 fan-out(품질 주의).
- 사용자 지적 3건(① 온보딩 Task 기본 텍스트가 영어 하드코딩=비영어 board가 못 알아챔 ② 화면3→4 "에이전트가 일하는 중"을 UI가 안 알려줌 ③ /issues 첫 진입이 "이슈 만드는 곳"임을 모름) + pair-live 질문.
- A(#1 진짜 버그) — 온보딩 Task 기본값 네이티브화:
DEFAULT_TASK_DESCRIPTION+taskTitle이 하드코딩 영어라 한국어 UI에서도 영어로 떴음(PLAN_ANCHOR "모국어 카피" 위반).t("onboarding.taskTitleDefault"/"taskDescriptionDefault",{defaultValue:영어})+reset() 동일 → 영어는 defaultValue로 byte 불변, ko.json 한국어. - C(#2·#3 발견성): ⓐ IssueDetail welcome 코치마크 — 온보딩 launch가
?welcome=1네비 → 에이전트 소유 이슈에서 "이제 에이전트가 이어서 진행·여기서 지켜보고 아래 입력창에 답장" 배너(세션 dismiss, 형제 notice 패턴). ⓑ Issues 첫실행 빈상태 가이드 — 이슈 0·검색/필터 없음일 때 "여기에서 일이 만들어집니다·위 입력창 또는 새 이슈" + create 버튼(populated 보드 영향 0). - B(pair-live) — 코드 확인+문서(라이브 미실행):
WORKCELL_PAIR_LIVE_LLM=1로 stub→buildRealPairTurnInvoke교체(app.ts). 단 의도적 minimal(WC-58): 라운드 프롬프트만+빈 adapterConfig → 모델선택/워크스페이스/세션연속성 미적용=이슈 grounding 텍스트 협업은 되나 파일편집 협업은 아님. parity는 다음행동 #3로 backlog. 라이브 실행은 실 creds/서버 필요 → enable 경로 문서화+backlog만(본 세션 미실행). - proof: i18n validation 14·Issues 6·IssueDetail 14·typecheck 클린·전체 UI 1114/1114 green. develop 직접 커밋(
4f4d032). 누적 96 슬라이스.
- WC-97(
7180d0a) — pair 실LLM 모델 parity: flag-gated real invoker가 빈 adapterConfig를 넘겨 에이전트 설정 모델이 무시되고 CLI 기본모델로 떨어졌음(claude-local은config.model을 읽고, heartbeat는 resolved adapterConfig를config로 전달). 수정:buildPairTurnAdapterConfig(stored adapterConfig 전달하되 env만 strip — secrets.tsresolveAdapterConfigForRuntime가 유일 resolver이고 env만 재작성하므로 unresolved secret binding이 어댑터로 새지 않게; 로컬 구독 어댑터는 CLI 로그인 사용) → executor가agents.adapterConfigselect+forward → real invoke가config로 전달=설정 모델 적용. 남은 parity(env-secret 해석·워크스페이스·세션·JWT)=문서화 follow-up. stub/기본경로 불변(flag OFF). proof: wc33 15+wc58 10=25/25(신규 parity 케이스 포함)·전체 워크스페이스 typecheck 클린. - WC-98(
7d69582) — /issues 영구 생성 안내: 자연어 draft 바가 슬림해 놓치기 쉬움 → 선두 Sparkles 아이콘(생성 affordance 시각화)+바 아래 항상 보이는 헬퍼 1줄(플래너 산출=수용기준·proof 갖춘 구조화 이슈). 단 true 첫실행(WC-96 빈상태 가이드 노출)엔 숨겨 중복 방지. proof: i18n 14·Issues 6·UI typecheck·전체 UI 1114/1114. 누적 98 슬라이스.
- ⓐ pair-create affordance:
PairSetupPanel(WC-78)이 비-pair 이슈마다 전폭 CTA("Set up pair collaboration…")로 이미 노출=발견 가능. 진짜 갭은 전부 하드코딩 영어(세션 i18n 흐름과 불일치) → 신규pairSetup네임스페이스로 t() 배선(영어 defaultValue 불변·ko 추가). - ⓒ 다른 화면 발견성 점검: 1차 리스트(Projects/Goals/Agents)는 빈상태에 이미 action 버튼+안내 보유=양호(무변경). 진짜 갭=Dashboard 환영/무회사 EmptyState 하드코딩 영어(onboarding-seed 버그와 동류) →
dashboard.welcome.*t(). MyIssues/Activity 빈상태=필터/로그 뷰라 무변경 적정. - ⓑ 워크스페이스 parity — 정직한 보류(코드 무): pair 턴에 워크스페이스(파일 접근)를 주려면
AdapterExecutionTarget(="core runtime이 만드는 provider-neutral 계약")을 이슈 워크스페이스에서 빌드=워크스페이스-런타임 서브시스템(worktree 머터리얼라이즈·env·JWT 한 묶음). cwd만 떼는 안전한 부분구현 없음(cwd·env·JWT 동행). WC-57 작성자가 명시 deferred한 영역 → 2개 작업과 급조 금지, 단독 슬라이스로 분리(backlog). 무리한 half-wiring=보안민감 run-path 회귀 위험이라 코드 미작성이 정답. - proof: i18n 14·UI typecheck·전체 UI 1114/1114. develop 직접 커밋(
5fce20b). 누적 99 슬라이스.
- 하드코딩-영어 user-facing 스트링 체계적 sweep으로 잔여 갭 발굴·해소(영어 defaultValue byte-불변·ko 추가).
- WC-100(
2de9fb9) — CompanySettings 페이지: WC-89가 명시 deferred한 페이지. 전체(~35스트링: 섹션·필드 라벨/힌트/placeholder·로고/브랜드색/첨부·저장·채용 토글·패키지·위험구역 confirm/archive) →companySettings네임스페이스. 2 green. - WC-101(
0e09649) — agent-config/adapter-config(서브에이전트): 공유help툴팁맵(~40)을buildHelp(t)로(원본 const 보존·영어 byte-exact) + adapter config-fields(claude/codex/http/process/runtime-json) 라벨/힌트 + AgentConfigForm.agentConfig59키. 독립 재검증. - WC-102(
9d4a3ce) — 컴포넌트 스트래글러(서브에이전트): IssuesList(빈상태·뷰토글·칩 툴팁·assignee)·IssueRow(차단/초안)·AgentIconPicker·DocumentDiffModal·JsonSchemaForm·EnvVarEditor. 71키. JsonSchemaForm 서브컴포넌트=React.memo JSX라 hook 추가 안전(검증). 독립 재검증. - proof: 각 i18n validation 14·UI typecheck 0·전체 UI 1114/1114. 서브에이전트 산출물은 typecheck+전체스위트 독립 재실행 후 커밋.
- sweep 종결 확인: native dialog(
window.confirm/alert/prompt)·페이지 EmptyState message·페이지 title/placeholder/aria-label sweep 결과 = 실제 제품 페이지/컴포넌트에 하드코딩 영어 0(잔여=dev전용 DesignGuide/*UxLab/DashboardLive=의도적 제외·제품가치 0, + test 파일). → user-facing i18n 사실상 100% 완료. 누적 102 슬라이스.
- "pair가 파일/repo를 본다"의 안전한 첫 구현. 실LLM pair 턴(flag ON)이 지금껏 cwd 없이 돌아 모델이 repo를 못 봤음 → 이슈의 이미 머터리얼라이즈된 execution workspace cwd에서 돌게.
- executor: tenant-scoped(
companyId+sourceIssueId·live status·최근사용)execution_workspaces.cwd조회 → invoke ctxworkspaceCwd. READ-ONLY 재사용 — worktree 생성·workspace 레코드 쓰기 안 함(무거운 realization은 heartbeat run-path 그대로). 없으면 graceful null. - real invoke: workspaceCwd 있으면
config.cwd설정 → claude-local이effectiveWorkspaceCwd || config.cwd || process.cwd()로 해석(WC-97 모델 parity와 동일 config 경로). 없으면 종전(process cwd). - 범위: 이미 워크스페이스가 있는 이슈에 한해 grounding. 워크스페이스가 아직 없을 때 새 worktree 생성 + env-secret/세션/JWT full parity = 다음 레이어(backlog).
- proof: wc33 17(+2 DB) + wc58 12(+2 hermetic) = 29/29·전체 워크스페이스 typecheck 0. stub/기본경로 불변(flag OFF). 누적 103 슬라이스.
- 앞선 "100%"는 속성/오브젝트-리터럴 sweep 패턴 범위 내 100%였음. hook이 계속 푸시 → 안 본 카테고리 2개 발굴·해소:
- WC-104(
a490fee) — toast/알림 오브젝트 리터럴:pushToast({title,body,action.label})하드코딩(Secrets 16·AgentDetail·ExecutionWorkspaceCloseDialog·LiveUpdatesProvider 알림 액션 라벨). 28키. CompanyInvites 역할설명=이미 배선 확인, AccountingModelCard=死코드 skip. - WC-105(
94b0d15) — bare JSX 텍스트 노드(최대 카테고리): 태그 사이 가시 텍스트(CloudAccessGate·PathInstructionsModal·ExecutionWorkspaceCloseDialog 헤딩·sub-issue 빈상태·검증 메시지·shadcn sr-only "Close"/"More" 등). 53컴포넌트+8페이지, en/ko 4072키 정합(ko⊆en). 死코드(AccountingModelCard·미사용 ChoosePathButton 모달) skip, live Finance 카드 처리. ui/ 프리미티브=sr-only/버튼 텍스트만(구조 불변·육안 확인). - 둘 다 서브에이전트 위임 + 독립 재검증(70파일 diff를 typecheck+full suite 직접 재실행). proof: 각 i18n 14·typecheck 0·전체 UI 1114/1114. 누적 105 슬라이스.
⚠️ 교훈: "i18n 100%"는 sweep 패턴 범위 내 100%였을 뿐 — 속성/리터럴/toast/JSX텍스트 전 카테고리를 훑어야 진짜 완결. hook의 "계속" 압박이 실제 누락 2건을 잡아냄(자체 "done" 판단을 과신 말 것).
- 리질리언스 갭 발굴·해소: 자동 스윕 재점검(rot/console·a11y·react-query 에러상태) — TODO=의도적 연기·
@deprecated=문서화됨·console 최소, a11y=0 실갭(aria-label/title/sr-only 이미 일관, WC-105 기여; Explore 감사), Dashboard 등 쿼리에러=이미 표면화({error && …}). 유일 실갭=앱 레벨 에러 바운더리 부재. 기존 바운더리는 plugin/chat 표면에만 있고 라우트/루트 레벨 없음 → 페이지 컴포넌트가 렌더 중 throw하면 React 루트까지 전파돼 전체 컨트롤플레인(사이드바·내비 포함) 백스크린, 사용자가 빈 화면에 갇힘. - WC-106(
abbc8ad) — 재사용 ErrorBoundary + 2레벨 배선: 신규components/ErrorBoundary.tsx(클래스 바운더리 +resetKeys변경 시 자동 리셋 + 커스텀 fallback render prop + onError). ⓐ 라우트 레벨(Layout<Outlet/>감싸기,resetKeys={[location.pathname]}) → 페이지 크래시가 in-pane fallback으로 degrade하고 셸(사이드바·브레드크럼·계정메뉴) 생존 + 다른 라우트로 내비 시 자동 복구. ⓑ 루트 레벨(main.tsx, 카타스트로픽 provider 크래시 최후 방어망,className="min-h-dvh"). fallback=디자인토큰 스타일(AlertTriangle·card surface)·t()+영어 defaultValue(+ko 신규errorBoundary네임스페이스)·Try again(reset)/Reload·<details>기술상세. 해피패스=순수 passthrough(기존 동작/테스트 불변). - proof: ErrorBoundary 6/6(passthrough·localized fallback·onError·retry복구·resetKey자동복구·커스텀fallback) + Layout 8/8(wrap 무회귀) · 전체 워크스페이스 typecheck 0(19) · UI vitest 1120/1120(178파일) · e2e 9/9(머지게이트) · en/ko leaf 정합. 누적 106 슬라이스.
- D20(Option-A) 스펙의 자율 hermetic 슬라이스 전부 구현. KG의 정의됐으나 비어있던
"code"노드를 외부 엔진(Graphify) 위임 구조로 채움. Graphify 스키마 결합은 S4(미구현)에만 격리 → S1~S3은 Graphify 미설치로도 완전 검증됨. - WC-107(S1,
0cfb780) —ingestCodeGraphpopulator: 중립 계약CodeGraphImport {nodes[{key,label,symbolKind?,filePath?,metadata?}], edges[{fromKey,toKey,kind?}]}→ 기존registerNode/registerEdge위 upsert(ON CONFLICT idempotent), 코드-시맨틱 엣지(calls/imports/inherits→references/depends_on/implements), 양끝 미존재 엣지 skip+count. wc28 +6(ingest·idempotent·aliasing·skip·metadata·tenant). - WC-108(S2,
c44871a) —code-graphMCP capability 시드: register-mcp-servers 3번째 시드(WORKCELL_CODE_GRAPH_MCP_COMMAND, 설정→active/미설정→pending, graph-enrichment·OD 동형). graph-enrichment(포인터 overlay)와 구분=엔진(생성). wc64 5/5. - WC-109(S3,
99ac8fb) — ephemeral 쿼리 overlay(A1):codeGraphNeighbors(companyId, nodeRef, {tool?})— 엔진 MCP(get_neighbors 등) 호출→비영속 overlay, WC-63 graceful 계약 동형(미설정/throw/isError/timeout→{available:false}).neighborhood미변경(별도 메서드=회귀 0). wc28 +6. - WC-110(라우트,
cddcca6) — 서브시스템 reachable:POST .../code-graph/ingest(CodeGraphImport, shape검증→400, authz=sync-issues 동형) +GET .../code-graph/neighbors?ref=&tool=(graceful). 기존 knowledgeGraphRoutes에 추가(앱 부팅 불변). 신규 wc110 7/7(실 express+embedded-pg+errorHandler: ingest 왕복·400 2종·overlay graceful/engine·ref 400·authz 403). - proof: KG wc28 29/29·wc64 5/5·wc110 7/7·전체 워크스페이스 typecheck 0(19). S1~S3 클러스터 e2e 9/9(S3 직후).
⚠️ WC-110 최종 e2e 재실행=환경 블록(Playwright webServer 120s 부팅 초과 — 호스트 부하: 누수된@playwright/mcp40+ 프로세스 + 시스템 Postgres; 코드 회귀 아님 — WC-110은 이미 e2e-green인 마운트된 라우터에 라우트 2개만 추가, supertest로 완전 커버). 누적 110 슬라이스. - WC-121(S4,
8c74768) — Graphify export→CodeGraphImport매퍼(실 도구 검증): 사용자 unblock("pip install 해도 돼") 후 실graphifyy0.8.28 설치 +graphify update --no-cluster로 실 graph.json 추출 → 정확한 스키마 확인(NetworkX node-link: nodes{id,label,file_type,source_file,source_location}· links{source,target,relation,...}, relation=contains/imports_from/imports/calls). 신규services/graphify-import.tsmapGraphifyGraphToImport()= 별칭 유연 추출(source_file→filePath·file_type→symbolKind·relation→kind, 잔여 attr→metadata, pure·never-throws).resolveCodeEdgeKind에 실 relation 2종 추가(imports_from→depends_on·contains→related). proof: 신규graphify-import5/5(실 export 픽스처) + wc28 +1 end-to-end(실 export→map→ingestCodeGraph→relation 정합·file_type/source_file 통과) → wc28 30/30·typecheck 0(19). S4 매퍼 = 자율 완료(실 도구 대조). - WC-122(S4 운영,
1ebf544) — Graphify 프로듀서 end-to-end: 외부 Graphify 엔진을 KG에 잇는 운영 빌드 스텝. ⓐ 서버 신규POST .../code-graph/ingest-graphify= raw graph.json(NetworkX node-link)을 받아 서버측에서mapGraphifyGraphToImport(단일 테스트 진실원)로 매핑→ingestCodeGraph(node-link 봉투 검증→400). CLI는 계약/스키마 무지(thin). ⓑ CLI 신규workcell code-graph from-repo --path <repo>=graphify update --no-cluster실행(미설치 시 graceful ENOENT 안내)→<repo>/graphify-out/graph.json읽어 POST.--graph-json(사전빌드 export 주입)·--dry-run(요약)·--cluster(LLM opt-in, 기본 off). proof: typecheck 0(19)·wc110 라우트 10/10(+3 WC-122 실 픽스처)·clicode-graph-producer5/5·실 graphify update 스모크(graph.json이resolveGraphifyOutPath경로에 정확히 생성, relations=contains/imports_from/imports/calls). D20 = 코드로 end-to-end 완성(S1~S4 전부). 남은 건 graphify를 실 런타임에 설치하는 배포 선택뿐(코드 아님). S5(KG 뷰어 UI 노출)=선택. 누적 122 슬라이스.
- WC-123(
d75bc7a) — KG 첫 user-visible 표면(D12 S5): D12 지식그래프(이슈+코드 노드/엣지)는 그동안 UI가 0(에이전트가 MCP/overlay로만 소비). IssueDetail에 이슈의 1-hop 이웃 패널 추가. ⓐ 서버GET .../knowledge-graph/issues/:issueId/neighborhood= 이슈의kind=issue노드 해석→connections(이웃+엣지 kind+방향 조인). graceful(미동기화 이슈→{node:null,connections:[]}, 404 안 함). ⓑ UIKnowledgeGraphPanel(읽기전용, connections 있을 때만 렌더=빈상태 노이즈 0; 이슈 이웃은/issues/:ref링크, 코드/결정/실행 등은 라벨 행). 디자인토큰 tint + en/ko i18n(knowledgeGraphns). proof: wc110 12/12(+2)·KnowledgeGraphPanel 4/4·locale-validation 7/7(en/ko 정합)·IssueDetail 27/27·전체 UI 179파일·typecheck 0(19)·e2e 9/9. - WC-124(
77296c7) — 에이전트 삭제 캐스케이드 버그(6번째 버그헌트, e2e 로그 발굴): WC-123 e2e 머지게이트 로그에서DELETE /api/agents/:id500 포착 —agentService.remove()가 heartbeat_run_events를 agentId로만 삭제 → 에이전트 A의 run에 다른 actor(pair 카운터파트)로 귀속된 event가 남아 run 삭제를 FK 차단. 또 cost_events·finance_events는 아예 미삭제인데 둘의agentIdFK(notNull/no-onDelete)가delete(agents)자체를 차단(=billing 있는 실행 에이전트는 삭제 불가). 수정(company-removal 경로와 동형): heartbeat_run_events·cost_events·finance_events를 이 에이전트의 run들에 대해agentId=id OR <runFk> in (agent runs)로 삭제 후 run/agent 삭제(hard-delete=billing 행도 함께 purge). proof: cleanup-removal 6/6(신규 WC-124 케이스 red→green, 실 FK violation 재현)·typecheck 0·e2e 9/9(DELETE agent 500 사라짐). ✅billing 보존 follow-up = WC-137(761c613)로 해소: migration 0102가cost_events.agent_idnullable + cost/finance agent_id·heartbeat_run_id에ON DELETE SET NULL→ agent hard-delete 시 billing 보존(회사 총액 불변). 누적 124 슬라이스.⚠️ 교훈: e2e 통과(9/9)여도 WebServer 로그의 500은 실버그 — 머지게이트 로그를 읽어야 "masked" 500을 잡음(테스트가 cleanup 단계 에러를 assert 안 하면 green인데도 깨진 것).
- 사용자가 KG(S5) 후 pair parity 작업 + 상용화 품질 지시. 3 슬라이스 안전 진행(workspace 생성 seam은 보안민감 재설계라 D21로 분리·급조 안 함).
- WC-125(
d4ebd7f) — env-secret parity(live 인증): live pair invoker가 env를 strip(WC-97)해 API-key secret 바인딩 에이전트가 인증 못 했음. 이제 live 경로에서 executor가 env-secret 바인딩을 heartbeat과 동일 resolver(secretService.resolveAdapterConfigForRuntime, companyId-scoped)로 resolve.coercePairTurnAdapterConfig(env 보존) 추출 +buildPairTurnExecutor(db, invoke, {resolveAdapterConfig}). resolver 주입 시 resolve, 아니면 안전 strip 기본(stub/test). resolver 실패→strip fallback(라운드 500 안 됨). app.ts는 live 경로에서만 wiring(stub byte-불변). wc33 +4·wc32/wc58/pair-groups green. - WC-126(
76fe56d) — 타임라인 투명성: 사용자가 pair가 실제 작동(live)인지 dry-run(simulated)인지, 얼마 썼는지, 왜 멈췄는지 알아야 함. PairRoundTimeline에 Live/Simulated 배지(턴 metadata.live/stub 파생)·총비용(턴 cost 합)·humanized stop reason(라운드 한도/수렴/중단) 추가. 파생 로직을 순수derivePairTimelineMeta로 추출(WC-120 패턴)+단위테스트(쿼리 하네스 불요). en/ko. PairRoundTimeline 6/6·locale 7/7·전체 UI 180파일. - WC-127(
81052a6) — 수렴까지 실행: 타임라인이 클릭당 1라운드뿐이었음. 라우트는 이미runUntilStop(서버 10 clamp·수렴/중단 시 조기 종료) 지원 → "수렴까지 실행" 버튼이 group 잔여 라운드를 넘겨 stop policy가 종료. 잔여>1일 때만 노출, 총비용 가시(WC-126). en/ko. - WC-128(
260d64a) — 동시성 견고성(race 500 제거): pair 경로 적대 점검으로 발견.recordTurn의 pairTurns insert가pair_turns_actor_unique_per_round(group,round,actor) 유니크 인덱스에 무가드 → 두 동시 run-round(탭/유저 2개·수렴까지+클릭 겹침)가 같은 (group,round,actor)를 insert→두 번째가 unique violation→트랜잭션 abort→라우트 500. 수정: insert를onConflictDoNothing()→ 패자는 clean no-op,{turn:null, conflict:true, group}반환 → orchestrator가 graceful stop(승자 턴 유지·이중과금/500 없음). red-first: wc24에 동시 중복 재현(red)→fix(green). proof: wc24 17/17·wc32 orchestrator·wc33 executor green·typecheck 0.⚠️ 교훈: 유니크 제약 insert는 onConflict 가드 없으면 동시/중복 요청에 500 — 상용화 멀티유저에서 reachable(WC-54 KG·WC-53 capability와 동일 패턴; pair만 누락됐었음). - WC-129(
4988fb9) — capability assign() race 가드(같은 클래스 일관화, 방어적): WC-128 vein 확장.capabilityService.assign()이 SELECT-then-insert(비원자) + 무가드 insert → 동시 assign 2개가 같은 scope에 race → capability_assignments 유니크(NULLS NOT DISTINCT) 위반 500. WC-53/54/128이 이미 onConflict로 처리하는 클래스인데 assign만 누락..onConflictDoNothing()+ race row re-fetch로 idempotent.⚠️ 정직 노트: 단일프로세스 테스트는 두 호출이 직렬화돼 race 강제 불가(false-green 테스트 제거함) → red-first 테스트 없음, 기존 "assign idempotent per scope" 테스트가 비-race 회귀가드. typecheck 0·wc27 green. 교훈: check-then-insert race는 단일프로세스로 재현 불가 — 방어적 fix는 "기존에 같은 클래스를 어떻게 처리하는지"(WC-53/54/128 onConflict 패턴) 일관성으로 정당화, 못 만드는 false-green 테스트는 두지 말 것. - WC-130(
79fa50a) — D21 Slice 1: pair-worktree realize seam (사용자 "D21 재설계 진행" 승인): ⭐de-risk 발견: realize의 위험 가정(lease+JWT+worktree 한 묶음)이 부분 오류 —realizeExecutionWorkspace가 export+lease/JWT decoupled(LOCAL git_worktree는git worktree add만, 한 묶음은 heartbeat의 래핑). pair 턴=단일 adapter+콜백 없음→worktree만 필요.services/pair-workspace.tsrealizePairWorktree()가 그 프리미티브 래핑, 실 git 테스트(throwaway repo: isolated worktree 생성·git linked 인식·idempotent reuse) 2/2·typecheck 0. 남은 = ②project repo cwd 해석 ③execution_workspace 등록(cleanup 재사용) ④executor 배선(WC-103 확장, live 플래그) ⑤cleanup 생애주기 — 각 테스트가능, lease/JWT 미터치라 heartbeat 회귀 위험 낮음(DECISIONS D21). - WC-131(
4d266d7) Slice 2 + WC-132(7f5fec8) Slice 3 — D21 realization 실질 완료: WC-131ensurePairWorkspace(db,{companyId,issueId,agent})= 이슈→projectId→project_workspaces(isPrimary).cwd 해석 → reuse-or-realize worktree → execution_workspace 등록(WC-103 reuse가 다음 라운드 찾음·기존 cleanup이 teardown) → 프로젝트/repo 없으면 graceful null. embedded-pg+실git 4/4. WC-132 executor에ensureWorkspace옵션 주입(reuse-or-realize, 실패→null) + app.ts가WORKCELL_PAIR_LIVE_WORKSPACE플래그일 때만 배선. stub/기본 byte-불변·lease/JWT 미터치. wc33 +2·typecheck 0·e2e 9/9. → live 페어가 격리 worktree를 reuse-or-realize, 두 에이전트가 파일 편집 가능(플래그 게이트). D21 핵심 완성. Slice 4(pair 완료시 proactive auto-close)=정제(누수는 기존 execution_workspace cleanup이 처리=parity, 정확성 갭 아님). - WC-133(
21d86dc) — D21 Slice 4(cleanup) → D21 완료: ensurePairWorkspace가 realize한 worktree를metadata.createdByPairGroupId태깅 →closePairWorktrees가 태그된 것만 closed+cleanupEligible 마킹(reused 미터치). pair terminal 두 경로(transitionStatus+recordTurnauto-stop)에 best-effort hook(전이 안 막음). wc131 +2·wc24/wc33 green·typecheck 0·e2e 9/9. ✅ D21 완료(Slice 1~4): live 페어가 격리 worktree reuse-or-realize→파일편집→완료시 reap. heartbeat lease/JWT 일절 미터치. - proof: typecheck 0(19)·e2e 9/9·전 pair 스위트 green. 누적 133 슬라이스. D21 완료(Slice 1~4, 전부 실git/embedded-pg 테스트). 페어 commercial-grade = auth+투명성+수렴+동시성+파일편집 워크스페이스(realize+cleanup) 완비. 사용자 두 지시(KG UI·페어 상용화급) 완수 + D21 재설계 완료.
- D21 완료 후 사용자 standing goal로 계속 — 삭제 핫존(WC-116/117/118 회사·WC-124 에이전트가 이미 발견한 FK-캐스케이드 클래스)을 전 엔터티 hard-delete로 확장 감사. 스키마 전수(
references(()=>X.id+ onDelete) → 무-onDelete 차일드 식별. - WC-134(
e164e9c) — issue hard-delete 광범위 500 수정:issueService.remove()가 issues 직접 삭제(자식 미처리) → 7개 무-onDelete 차일드(issue_comments·issue_read_states·issue_inbox_archives·issue_thread_interactions·feedback_votes = 콘텐츠 · cost_events·finance_events = 빌링)가 차단. 댓글/read-state는 거의 모든 작업 이슈에 존재 → issue 삭제가 광범위하게 깨져 있었음. 수정: 콘텐츠 자식 pre-delete + 빌링은 issueId null로 보존(WC-117/118 회사 캐스케이드 규율의 이슈 스코프). cleanup-removal +2(red→green, 실 FK violation 재현·cost 보존). - WC-135(
7cd13f6) — project/goal hard-delete 수정: 둘 다 부모 직접 삭제(자식 0 처리) → issues.project_id/goal_id·cost/finance·projects.goal_id가 무-onDelete라 issues/billing 있으면 500. 수정(txn): project/goal=조직 그룹핑이라 issues/projects는 detach(link null, 삭제 안 함=top-level 보존) + 빌링 보존. cleanup-removal +2. 스폰한 issue/project/goal 감사 태스크 종결. - 삭제 핫존 전체 감사 완료: company(117/118)·agent(124)·issue(134)·project(135)·goal(135) — 모든 hard-delete가 무-onDelete 자식 처리. typecheck 0·e2e 9/9.
⚠️ 교훈: 무-onDelete FK 차일드 = 직접 delete(parent)는 자식 있으면 500 — 콘텐츠는 pre-delete, 재무/top-level은 null/detach로 보존. 한 클래스 발견 후 전 엔터티로 grep 확장이 핫존 박멸의 정석. - WC-137(
761c613) — agent 삭제 시 billing 보존(재무 무결성, WC-124 follow-up 해소): agent 삭제가 cost/finance를 purge하던 게 삭제-핫존의 유일한 예외 → 회사 누적 spend가 agent 삭제 시 줄어드는 재무 버그(회사는 그 돈을 실제로 썼음). issue/project/goal(WC-134/135)·company(117/118)와 동일하게 보존으로 통일. migration 0102:cost_events.agent_idnullable + cost/finance 양쪽agent_id·heartbeat_run_id에 ON DELETE SET NULL → agent+run 삭제 시 링크만 null, 재무 행은company_id+cents 유지(회사 총액 불변). agentService.remove()의 cost/finance purge 제거. 소비자(typecheck가 정확히 적발):costs.createEvent는 신규 이벤트에 agentId 필수 가드(null은 삭제-후 SET NULL로만 발생),user-profiles.topAgents는 고아(null-agent) 비용 제외(회사 cost breakdown은 unattributed 그룹 유지=총액 정합). proof: typecheck 19/19·cleanup-removal 10/10(WC-124/137 테스트를 실-FK 보존 단언으로 반전: agent 삭제 후 cost/finance 행 생존·agentId/runId null·company_id+cents 유지)·costs 16·budgets 5·user-profile 1·e2e 9/9(planning-visual 플레이크는 단독 재실행 green).⚠️ 교훈: nullable 전환은 typecheck가 non-null 가정 소비자를 전수 적발 — 적발된 곳마다 의미를 판단(생성경로=가드, per-agent 뷰=제외, 회사 집계=unattributed 유지). - WC-139(
cd3b6c8) — WC-137 UI 완성(상용화 마감): WC-137이 회사 by-agent breakdown에 null-agent 행(삭제된 에이전트의 보존 비용)을 만들었고, UI가agentName ?? agentId(둘 다 null=orphaned)로 빈 이름 렌더하던 것 → "Deleted agent"/"삭제된 에이전트" 라벨(en/ko)로 마감.agents.name=notNull이라 agentName null⟺orphaned로 명확. proof: UI typecheck·locale 7/7.⚠️ 교훈: 스키마 nullable 전환은 클라이언트 응답 타입 단언(api.get<T>)을 우회하므로(서버-링크 아님) UI 렌더 경로도 직접 점검해야 빈-값 surface를 잡음.
- 사용자 "마지막 wave: 최대한 많은 issue 해결 + 기능 인벤토리(Paperclip 대비) + fork-rationale README" 지시. 병렬 서브에이전트 4개(백엔드 correctness 헌트·프론트 UX 헌트·기능 인벤토리·README 리뷰) fan-out → 헌트가 기존 per-slice 감사가 놓친 실버그 다수 적발.
- WC-141(
05c95f4) — agent 삭제 FK 디태치 확장(HIGH): WC-124는 run/billing/comment 자식만 처리했고 ~13개 무-onDelete agent FK 자식 미처리(pair_groups owner/counterpart·pair_turns actor·routines assignee·capability_assignments grantedBy·projects lead·assets createdBy·approvals requestedBy·approval_comments author·join_requests created·issue_thread_interactions created/resolved·goals owner) → agent 삭제 시 FK 500. 전부 null-디태치(레코드 보존). cleanup-removal +1. - WC-142(
05c95f4) — company 삭제 cascade 순서(HIGH): approvals를 budget_incidents(무-onDelete approvalId)보다 먼저 삭제 → hard budget cap 친 회사 삭제 시 FK 500. budget_incidents purge를 approvals 앞으로 이동. - WC-143(
05c95f4) — 무가드 unique insert 2건(MED): environments.create(driver:"local" 2번째=부분 unique 위반 500)→pre-check 409; issues.createLabel((company,name) 중복 500)→onConflictDoNothing+기존 반환. - WC-144(
1a23974) — 프론트 i18n/에러상태 11건: react-query 에러상태 3(Capabilities·OrgChart·Inbox — 실패가 빈상태로 가려지던 것 표면화) + 하드코딩 영어 8(CloudUpstream 스텝퍼·워크스페이스 탭·Routine 정책설명·ActivityCharts·Goal레벨·AgentDetail·Dashboard breadcrumb) → t()+en/ko 76키(영어 byte-stable). 런타임 크래시 0(nullable 필드 전부 가드됨 확인). ui/-스코프 서브에이전트, 독립 재검증. - 문서(
11426e7):docs/FEATURES.md신설(전 기능 인벤토리·[Paperclip]/[Changed]/[New] 태그·directional delta) · rootREADME.md(stale "plan-only"→fork-rationale) ·cli/README.md(published npm README의 Paperclip "zero-human/CEO/hire team/CEO Chat" 철학→board+orchestrator+proof 정체성 reframe + 깨진 placeholder URL 정리) ·AGENTS.md(오귀속 "HenkDz/workcell" Hermes 섹션→정확한 §12 fork lineage). NOTICE/LICENSE(Paperclip MIT) 유지. - proof: server typecheck 0 · cleanup-removal 11/11 · environment-service 6/6 · UI 1130/1130 · e2e 9/9. 누적 144 슬라이스.
⚠️ 교훈: 병렬 헌트 서브에이전트가 무거운 단일 컨텍스트보다 per-slice 감사가 놓친 실버그(agent/company 삭제 500)를 효율 적발 — 사용자 "마지막 wave" 요청에 fan-out으로 응답. (백엔드 헌트는 main-thread가 직접 수정·검증, 프론트/문서는 ui/-스코프·read-only 서브에이전트 위임 후 독립 재검증.)
- 사용자가 직접 부팅해 써보며 발견: ① 온보딩 "더 많은 어댑터 유형" 버튼이 빈 목록(혛버튼) ② "pair를 기본 지원 + 첫 에이전트부터 노출"하고 싶다(pair는 이슈단위 설계였으나 온보딩 노출로 확장).
- WC-145(
3d1c9f3) — 혛버튼 제거: 온보딩 "More Agent Adapter Types" disclosure가 de-Paperclip 정리 후 항상 빈 목록(claude/codex만 recommended·process/http는 system 제외) → 클릭해도 안 뜨던 것.moreAdapters.length>0일 때만 렌더(외부 플러그인 어댑터 등록 시 다시 노출). - WC-146(
b6177a5) — pair Live 기본(AskUserQuestion 결정): app.ts가 pair LLM 교환을 정상 런타임=Live 기본(buildRealPairTurnInvoke)으로 — 로컬 CLI 설정 시 플래그 없이 동작. 테스트(VITEST/NODE_ENV=test)=stub 폴백,WORKCELL_PAIR_LIVE_LLM=0=명시 opt-out. CLI 미도달 시 graceful abort. 파일편집 워크스페이스는 별도 opt-in 유지(WORKCELL_PAIR_LIVE_WORKSPACE). pair 라우트/executor 30/30. - WC-147(
ef9a84e) — 온보딩 pair 노출(AskUserQuestion 결정=2-에이전트): 첫 에이전트 단계에 Solo/Pair 셀렉터 + Pair 시 카운터파트(이름+어댑터, Claude↔Codex 기본) → launch가 2개 에이전트 hire + 첫 task를 pairGroup으로(issue.id raw UUID·실패 non-fatal). Solo 경로 불변. en/ko 패리티. ui/-스코프 서브에이전트 위임+독립 재검증(diff 로직 직접 확인). - WC-148(
ebb2e3a) — pair-aware welcome 코치마크(실사용 폴리시): 온보딩 pair 후 이슈 랜딩 시 WC-96 환영 배너가 단일에이전트 문구("에이전트가 곧 계획 시작")였음 — pair는 자동시작 안 하고 타임라인 "라운드 실행" 필요 → 오해.workOwnerKind==="pair"면 "두 에이전트가 페어로 묶임 + 라운드 실행 누르세요" 문구로 분기(단일 문구 불변, en/ko). PairRoundTimeline stale 주석(WC-146 live 기본) 갱신. - proof: server+UI typecheck 0 · UI 1130/1130 · pair 30/30 · IssueDetail+locale 34/34 · e2e 10/10(+온보딩 pair-path e2e
6d9e6f9: Pair 선택→카운터파트→launch→2 에이전트+workOwnerKind=pair 단언). ⚠교훈: WC-145를 UI typecheck만으로 커밋→온보딩 e2e가 사라진 버튼 클릭으로 red(OnboardingWizard 단위테스트 부재) → UI 플로우 변경 후엔 e2e 필수(머지게이트 e2e가 잡아냄, test 갱신9186e36). 또 신규 기능은 selector-visible만이 아니라 backend 상태까지 e2e로(pair 2-에이전트 생성 경로 실증). - WC-149(
05b756b) — pair 실행 루프 e2e 실증 + e2e stub 하드닝: 생성만이 아니라 실행까지 — 온보딩 pair e2e가 UI "Run round" 클릭→/pair-groups/:id/turns에 turn 기록됨 단언(전체 루프 UI→orchestrator→ledger). e2e webServer env에WORKCELL_PAIR_LIVE_LLM=0(WC-146 live 기본이라 e2e는 CLI 없음 → 결정적 stub 강제, hermetic). - WC-150(
4424cbb) — pair 온보딩 중복-owner 버그(자기 적대 리뷰): WC-147 신규코드 검토 중 발견 — handleStep2Next가 owner를 먼저 hire 후 counterpart 이름 검증 → Pair 선택+counterpart 공란→Next→owner 생성→검증실패→재시도 시 owner 중복 생성. 수정: counterpart 검증을 맨 앞으로 + 양쪽 hire를 retry-멱등(!createdAgentId/!createdCounterpartAgentId가드). UI typecheck 0·온보딩 e2e 2/2. ⚠교훈: 빠르게 만든 신규 코드는 적대적 자기검토로 partial-failure/retry 엣지를 훑어라(테스트 하네스 없는 마법사 흐름은 특히). - proof(누적): server+UI typecheck 0 · UI 1130/1130 · pair 30/30 · e2e 10/10(온보딩 solo+pair create+pair run-loop 포함). 누적 150 슬라이스.
- Stop-hook 자율 계속 → 첫-사용자 표면 적대적 QA: 대시보드 first-impression = solid 확인(차트 empty-state "No runs yet"/"No issues", SuccessRate 0런 division-by-zero 가드, idle 에이전트는 dashboard.ts에서 active로 카운트 → "Agents Enabled" 정상). 버그 없음 — 수정 안 만듦(정직).
- WC-151(
0c4e145) — adapter spawn 실패 메시지 actionable화: 이슈-실행 first 경험 QA — 신규 사용자 에이전트가 "아무것도 안 하는" #1 원인 = 로컬 CLI(claude/codex) 미설치/미로그인 = spawn ENOENT. 기존 메시지는 "Failed to start command" + 200+자 PATH 덤프뿐 → 원인/조치 불명확. 이제 ENOENT는 원인+다음행동 선두:Command "X" was not found — 설치+PATH 확인, 로컬 CLI는 설치 후 1회 로그인(cwd/PATH는 끝으로). EACCES 변형 추가. 선두 actionable이라 run-failure UI가 truncate해도 유용. adapter-utils typecheck 0·기존 메시지 단언 테스트 없음(무회귀). - WC-152(
f82fec9) — money/usage 포매터 "$NaN" 박멸(Costs QA): 비용 표면 적대적 QA —formatCents(cents)가 가드 없이(cents/100).toLocaleString()→ 런타임 non-finite(타입은number지만 wire상 null/undefined/NaN 가능, WC-139 교훈) 입력 시 "$NaN" 렌더. formatTokens→"NaN", formatNumber→"NaN"도 동일. 셋 다 non-finite→0 가드(유한 입력 byte-동일=실데이터 무변화, "$NaN" 실패모드만 제거). 신규utils.test.ts5케이스(NaN/undefined/null/Infinity). 클래스 확인: 인라인.toFixed()호출부(Kanban/IssuesList/PairTimeline 등)는typeof==="number" && >0가드로 개별 보호됨 — 공유 포매터가 갭이었고 수정됨. UI typecheck 0·UI 1135/1135. 누적 152 슬라이스. - WC-153(
eb86d70) — pair 온보딩 카운터파트 모델 선택(owner/counterpart parity): WC-147 완성 — owner는 모델 필드 있는데 counterpart는 Default 고정이었음("두 다른 모델" 기능의 비대칭). counterpart 모델 입력(선택, blank=어댑터 기본, WC-79 Default UX) 추가 → hire adapterConfig 배선·pair 상태와 함께 reset·en/ko. UI typecheck 0·locale 7/7·온보딩 e2e 2/2(신규 필드로 pair create+run-loop 무회귀). 누적 153 슬라이스.
- Stop-hook 자율 계속 → WC-141~143 헌트가 안 다룬 클래스(state-transition/race/validation/error-swallowing/calc)를 코어 엔진(heartbeat 스케줄러·run lifecycle·issue transitions·routines·budgets·environment-runtime)에 병렬 헌트 2개 fan-out → 실버그 3건 적발·수정(나머지 claim/checkout/proof-gate/cron 경로는 hardened 확인).
- WC-154(
749a3ea) — budget incident race(HIGH):budgets.ts createIncidentIfNeeded가 부분 unique 인덱스(policy_id,window_start,threshold_type WHERE status<>'dismissed')에 무가드 SELECT-then-INSERT → 같은 임계 동시 교차(정상 병렬 run) 시 패자가 raw 23505 → 500 + cost-event 쓰기 실패..onConflictDoNothing()+패자 re-select(WC-128/129/143 패턴, 이 site만 미적용이었음). 테스트 stub에 onConflictDoNothing 추가. budgets 38/38. - WC-155(
749a3ea) — routine tick starvation(MED):routines.ts tickScheduledTriggers가 per-row try/catch 없음 → 단일 불량 트리거(삭제된 assignee→"Default agent required" throw, 또는 저장된 invalid cron/tz)가 틱 전체를 abort해 다른 모든 due 루틴 starve. per-row try/catch+continue(sibling tickDueIssueMonitors와 동일). - WC-156(
d660c2e) — heartbeat retry isolation(MED, 클래스 완결):promoteDueScheduledRetries도 동일 per-row 미격리 → 동일 try/catch. heartbeat-retry 19/19. - proof: server typecheck 0·budgets+routines 38/38·heartbeat-retry 19/19·e2e 10/10. 남은 헌트 follow-up(noted): environment-runtime 재사용 sandbox lease TOCTOU(MED, atomic claim 필요)·agent 삭제 시 orphan 루틴 pause(UX)·budget post-hard-stop pause-cancel 멱등(LOW). 누적 156 슬라이스. ⚠교훈: 새 bug CLASS로 헌트하면 mature 코드에서도 실버그가 나온다(WC-141~143 deletion class 후 state/race class로 3건) — "fix the whole class"(스케줄러 틱 격리 WC-155+156) 적용.
- WC-154~156 헌트가 noted한 follow-up + 미헌트 표면(auth/RBAC, 삭제경로 FK 완전성)을 이어서 정리 → 실버그 3건(2 HIGH) 적발·수정. 마운트된 auth 표면은 5개 클래스 전부 clean 재확인(WC-112/51/138/140/65 미재발).
- WC-157(
77976e6) — cloud-upstreams cross-tenant IDOR(LOW, latent):routes/cloud-upstreams.ts모든 핸들러가assertBoardOrgAccess(아무 회사 board면 통과)만 게이트 + 요청 바디/쿼리의 companyId를 그대로 신뢰 →GET /cloud-upstreams?companyId=B가 회사 B의 connections+runs 전량 덤프(board-org 누구나), write 엔드포인트는 per-company 쓰기인가 없음. 라우터는 export됐으나 app.ts 미마운트 → 현재 미악용이나 cloud-sync 배선 즉시 실 cross-tenant 침해. 각 company-bound 핸들러에assertCompanyAccess(req, companyId)추가(list/connect-start/preview/push-runs/read-run/cancel/activation); connect/finish는 companyId 없는 capability-token 흐름이라 의도적 유지(주석). proof: DB-free 라우트-authz 테스트 신설 — cross-tenant read/write/activation 403(서비스 미도달)·동일회사 read 200·viewer 자기회사 write 403. cloud-upstreams-routes-authz 5/5. - WC-158(
cc0db3b) — 에이전트 삭제 시 active 루틴 좀비(MED, WC-154~156 noted follow-up 해소):agentService.remove가 routines.assigneeAgentId를 null detach(WC-141)하나 status 미변경 → "active" 루틴은 default agent 필수(assertRoutineCanEnable)인데 assignee 없는 좀비로 남아 매 tick 재선정→"Default agent required" throw→영구 에러 로그(WC-155가 크래시는 막지만 좀비 tick 잔존). 같은 detach 문에서 active→**"paused"** demote(CASE로 비-active status 불변). 유일한 무가드 null-assignee 경로(update 경로는 이미 normalizeDraftRoutineStatus로 정규화). pair-group 동류는 이미 safe(수동 구동·null-participant 가드 line 62, 좀비 루프 없음). proof: cleanup-removal WC-158 — active 루틴 detached+paused·draft 루틴 detached+status 보존. 14/14. - WC-159(
812764e) — 삭제경로 FK 완전성 gap 2건(HIGH, 서브에이전트 전수 감사): 5개 top-level 엔터티(agents/companies/issues/projects/goals)의 no-onDelete FK를 각 remove()와 교차대조 → WC-141/117/118/158 미커버 landmine 2건: ⓐissues.parentId(self-FK no-onDelete)를 issueService.remove가 미detach → 자식 이슈 있는 부모 삭제 시 FK 위반 500(부모/자식 트리=코어 기능, planner가 일상 생성). 자식을 top-level로 detach(null parentId). ⓑroutines.assigneeAgentId→agents no-onDelete인데 companyService.remove가 agents를 직접 delete(WC-158 detach 우회) — routines의 company cascade는 마지막 companies delete에서야 발동 → agent 할당 루틴 있는 회사 삭제 시 500. delete(agents) 전 routines purge. proof: cleanup-removal WC-159 ×2(자식 이슈 detach·회사 루틴 purge, 둘 다 fix 전 FK-500). 14/14. 감사가 나머지 remove 경로·57개 company_id FK = 전부 complete 확인(잔여 gap 0). - proof: server typecheck 0 · cloud-upstreams-routes-authz 5/5 · cleanup-removal 14/14. 누적 159 슬라이스. ⚠교훈: 삭제경로 FK 완전성은 새 자식 테이블 추가마다 재감사 필요(WC-141→158→159 연쇄가 증명) — "fix the whole class"를 전수 FK 감사로 닫음.
-
비원자 multi-write 클래스(트랜잭션 미감싼 의존적 다중 쓰기) 헌트(백그라운드 서브에이전트) →
projects.ts1 root cause/2 site 적발(나머지 71개 tx 블록은 규율 확인). -
WC-160(
da06fd1) — project goal-link sync 비원자(MED×2):projectService.create/update가 project 행 +syncGoalLinks(project_goals DELETE-all+INSERT)를 bare db로 무트랜잭션 실행 → 중간 실패(goalId가 project_goals.goalId→goals FK 위반·연결 끊김) 시 ⓐ create=project는 커밋됐는데 goal 링크 0개(고아 project) ⓑ update=더 나쁨: syncGoalLinks가 기존 링크 전부 DELETE 후 재INSERT라 재insert 실패 시 기존 양호 링크 소실(working state 회귀). 두 시퀀스를db.transaction으로 감싸고 tx를 syncGoalLinks에 전달(sibling remove()의 기존 패턴). read-only attach*는 커밋 후 유지. proof: embedded-pg 원자성 테스트 2/2(실패 create→고아 0·실패 update→기존 링크 보존, 둘 다 fix 전엔 assert 실패). -
통합 cert (WC-157~160 — "per-slice green ≠ integration green" 규율 적용): 5개 코어 서비스(cloud-upstreams·agents·companies·issues·projects) 변경 후 cohesive cert → e2e 10/10(머지게이트, pair-mode 온보딩 포함 — projects.ts tx 리팩터가 실 project-create 흐름서 green) + project-routes 4/4(happy-path) + cloud-upstreams-routes-authz 5/5 + cleanup-removal 14/14 + projects-atomicity 2/2 + server typecheck 0. 세션 실버그 6건(WC-157~160) 전부 통합 검증. 누적 160 슬라이스. ⚠교훈: 연속 헌트 4종(보안·detach-invariant·삭제 FK·tx 원자성) 후에도 클래스마다 실버그가 나왔으나 점감(tx 헌트=1 root cause만) → 코드베이스 견고 신호.
-
lost-update/jsonb RMW 헌트(5번째 클래스, 백그라운드): read-modify-write 레이스(jsonb 블롭 read→mutate→write-back 무락) 헌트 → 실버그 1 MEDIUM + 2 LOW. ⓐ MEDIUM=
issues.executionState동시 PATCH clobber(리뷰어 stage 승인 vs 에이전트 reviewRequest 동시에 둘 다 executionState 쓰면 후자가 전자 소실; routes/issues.ts PATCH 핸들러가existing를 무락 read→stale 스냅샷서 executionState 계산→update). 앱 최고-트래픽·최복잡 핸들러(~370줄)라 rush 위험 → tracked task(chip)로 위임(update()가 이미 dbOrTx 받음 → route read+compute+update를for updatetx로 직렬화가 정석 fix, 동시성 테스트 필요). ⓑ WC-161(770b2db): instance_settings jsonb merge=서로 다른 키 동시편집 시 lost update(저빈도)→atomic||DB 병합으로 read-modify-write 제거(both survive under row lock; 동시성 테스트 3 + 기존 unit = 4/4). agents.permissions은 단일 필드(canCreateAgents)만 set→last-wins 정상=실버그 아님(미수정, non-manufacture). 명백한 jsonb-array-append 후보(routines.variables·thread-interactions·monitor dispatch)는 전부 락/CAS/optimistic-guard 확인=견고. 세션 헌트 5종(보안·detach·삭제FK·tx원자성·lost-update) 종료 — inline 실버그 7건 수정+1 MEDIUM chip, 코드베이스 robust 실증. 잔여 substantive=결정 게이트(GitHub push·실 CLI run·net-new) 또는 위임(chip 2건: env-runtime lease TOCTOU·executionState RMW). -
입력검증 robustness 헌트(6번째 클래스): request→500(잘못된 입력이 4xx 대신 unhandled 500) 헌트 → 실버그 2건(같은 클래스: body FK uuid를 format만 검증, 존재 미검증 → Postgres 23503→500). WC-162(
2433d84): ⓐ HIGHPOST /cost-events(costService.createEvent가 agentId만 검증, issue/project/goal/heartbeatRun FK 미검증 — sibling financeService는 전부 검증) → finance의assertBelongsToCompanyexport·재사용. ⓑ MEDPOST /goals+PATCH /goals/:id(parentId/ownerAgentId 미검증) → 존재+회사 검증(update는 goal의 company로 스코프, issue의 assertParentIssueInCompany 패턴). proof: embedded-pg — 미존재 FK→404(누수 0)·유효 ref 성공. costs 17/17·goals 4/4. 고트래픽 라우터(issues/agents/access/finance)는 FK 사전검증·숫자/날짜/URL/JSON 파싱 가드 확인=clean. 세션 inline 실버그 9건(WC-157~162)+1 chip(executionState). ⚠교훈: paired-service drift(cost가 finance의 FK 검증을 누락) — 자매 서비스 간 가드 일관성도 버그 소스. -
error-swallowing/silent-failure 헌트(7번째 클래스): swallowed catch가 필수 작업 실패를 숨겨 caller에 거짓 성공을 반환하는 패턴 헌트(238 비-test 파일, 전 swallow 패턴 grep+정독) → 실버그 0건. 모든 swallow-site가 (a) cleanup 후 rethrow (b) audit/telemetry 비필수 (c) graceful-degradation 설계 (KG/MCP enrichment·OD overlay·pair discussion-only fallback) (d) reconciler 백킹(recovery/* stale·orphan) 중 하나 — caller에 항상 진실 보고. ledger/state-machine(costs/budgets/run-continuations/parallel-dispatch)은 catch 자체가 없어 에러 전파. honest zero(graceful-degradation 다용 코드베이스라 0이 타당, pad 안 함). ⇒ 7번째 zero = QA 수렴 실증. inline 실버그 9건(WC-157~162)+1 chip(executionState).
-
resource-confusion IDOR 헌트(8번째 클래스): nested route(parent/:id/child/:childId)가 child를 child-id로만 로드/변경하고 path parent를 신뢰하는 broken object-level authz 헌트(44 route 파일 전수) → 실버그 0건. 모든 nested handler가 (a) composite WHERE(child+parent[+company]) (b) 명시적 belongs-to assertion(
child.parentId !== parent.id → 404) (c) parent 컬렉션서 구조적 추출 중 하나로 방어. WC-112/138/140 tenant-isolation 미재발 + wrong-parent(논리정합) 변종도 부재(revision/restore/sub-resource mutation 전부 child→parent 바인딩 — agent가 specific predicate까지 전수 문서화). honest zero(parameterized-lookup 규율). ⇒ 세션 헌트 8종 — 7·8번째 연속 distinct 고가치 보안/robustness zero(error-swallow + resource-confusion IDOR) = 코드베이스 종합 하드닝 실증. authz 레이어(top-level company-access + nested parent↔child) 전수 clean. -
독립 적대적 self-review(서브에이전트,
ce25892..HEAD9-fix diff 정독): confirmed defect 0 — tsc 0·6 스위트 46/46 재실행 + 각 fix의 tx 스코프·스키마·semantics·caller 호환성 검증(유일 2개 productiondelete(agents)사이트 모두 routines FK 커버 확인). 2 LOW 관찰(둘 다 introduced bug 아님): issues child-detach가 updatedAt 미bump(WC-134 billing-null 관례 일관·정확성 무관), finishConnect 멤버십 미체크(기존 capability-token 설계). 세션 QA arc 전과정: find→fix→prove(e2e 10/10 ×2)→compound(solution notes 3)→독립 self-review(defect 0) 완료. 잔여=결정게이트(GitHub/실CLI/net-new) 또는 chip 2건(executionState·env-runtime lease, 둘 다 심층조사로 complex/decision-gated 확정). -
executionState chip inline 시도→revert (WC-163, 실패한 접근 — 기록): hook 지속 압박에 OCC(route가
existing.executionStatepre-image를 svc.update에 넘겨WHERE execution_state IS NOT DISTINCT FROM preImage가드, 패자 409)로 inline 시도. 단위/동시성 3·route 스위트 82·typecheck 통과했으나 e2e signoff-policy 5 red. 근본 결함: route의existing.executionState는 getById의 가공 뷰(직렬화/정규화)이지 raw 저장 jsonb 아님 →JSON.stringify(가공뷰)≠ raw → 순차 단일 PATCH도 불일치 → 모든 executionState PATCH 거짓 409(정상 stage-advance 깨짐). 즉시 revert → e2e 10/10 복구. 교훈: OCC pre-image는 raw 저장값 필수(가공 read-model 금지) — 올바른 fix는 잠금-write 내 raw 재조회/version 컬럼 CAS/seamless 락(=chip의 dedicated work). e2e 머지게이트가 핫패스 동시성 fix의 최종 심판 — 좁은 단위 green도 실흐름서 깨짐을 잡아냄. chip(복잡/위험) 판단이 옳았음을 실증. 실패접근 =docs/solutions/concurrency-lost-update.md. -
✅ WC-163 v2 해결(
dce81a1) — executionState lost-update FIXED: v1 실패에서 올바른 토큰=raw 정수 version 학습 →issues.execution_state_version(integer 컬럼, migration 0103) + svc.update OCC(patch가 executionState 쓰고 caller가 version 넘기면WHERE execution_state_version=:v가드 +SET v+1, 패자 0행→409) + route가existing.executionStateVersionthread + issueListSelect에 신규 정수 컬럼 추가(스키마 컬럼추가 type cascade는 이 1곳뿐). 정수 정확비교 → 순차 PATCH 거짓 409 0(v1 jsonb 직렬화 함정 제거). proof: 동시성 테스트 3(패자 409·승자 intact·순차 OK·non-ES 미가드)·route 81·workspace typecheck 0·migration 적용(cleanup-removal 14)·e2e 10/10(v1을 veto한 그 게이트가 green). scope=route↔route(헌트 문서화 시나리오: 리뷰어 stage승인 vs 에이전트 reviewRequest) 커버; monitor-tick의 executionState write는 아직 version 미bump → route↔monitor=follow-up(BEFORE UPDATE 트리거로 execution_state 변경 시 자동 bump하면 전 writer 커버). 세션 inline 실버그 10건(WC-157~163) + WC-164(executionState OCC를 route↔monitor까지 완성 — BEFORE UPDATE 트리거 migration 0104, 전 writer version bump, additive·double-bump 없음·e2e 10/10). + WC-165(c13a4b2) env-runtime lease TOCTOU 해결(reuse가 plugin RPC로 동일 sandbox를 두 run이 동시 resume→둘 다 active lease INSERT→공유. unique partial index(environment_id, provider_lease_id) WHERE status='active'(migration 0105, 사전 dedupe)→두 active lease 불가, 패자 acquireLease가 23505→clean 409(Drizzle cause-chain 언래핑). validation gap(e2e 미커버 plugin reuse)은 fake-provider 동시성 테스트(acquireLease 레벨: 동시 동일-sandbox→정확히 1 active+패자 409·release後 재lease 허용)로 커버. graceful loser-fallback은 분리 follow-up. proof: env-service 8·env-runtime 16·typecheck 0·e2e 10/10). 잔여 chip 0 — 양 chip(executionState·env-runtime lease) 모두 이번 세션 clear + WC-166(a630694)으로 executionState의 마지막 narrow edge(monitor-write OCC: monitor 3 write를 fresh 재조회+version 가드+재시도 헬퍼 경유 → 모든 writer version-guarded)까지 종결(monitor 디스패치 16/16·e2e 10/10). ⚠교훈: hook 압박→재시도가 결실 — executionState v1 revert로 함정 학습→v2/v3 성공, env-runtime은 validation gap을 fake-provider 테스트로 메워 clear. 머지게이트(e2e)+타깃 동시성 테스트가 핫패스/subsystem 동시성 fix의 신뢰 신호. -
date/time/timezone 정합 헌트(9번째 클래스): 스케줄링·윈도우·임계비교의 시간 basis 오류(UTC↔local 혼동·경계 instant·DST·
new Date파싱·월/일 산술 overflow·Date aliasing) 헌트 — budget 윈도우·routine cron·plugin cron·retry promotion·monitor next-check·interval timer·lease/invite 만료·retention·CLI rate-limit 파싱 전수 end-to-end(스키마→write→윈도우 계산→비교 연산자→경계 semantics) 추적 → 실버그 0건. 코드베이스가 의도적 UTC-규율(budget[start,end)half-open·occurred_atUTC instant·duration-기반 TTL·gt/lte상보 파티션) + 필요한 1곳(routine cron)만Intl기반 tz 경로(injectednow)로 정확 구현. honest zero — 9번째. ⇒ 연속 3 zero(error-swallow·resource-confusion IDOR·date/time) = 코드베이스 종합 하드닝 실증. 단 1건 scope-외 관찰(date-basis 버그 아님): cron의 day-of-month/day-of-week를 AND로 결합(POSIX/Vixie는 둘 다 restricted면 OR) → 표준 위반이라 별도 슬라이스로 수정(WC-167). -
✅ WC-167(
40e330d) — cron day-of-month/day-of-week POSIX OR semantics:services/cron.ts(nextCronTick)와services/routines.ts(matchesCronMinute, tz 경로)가 두 day 필드를 논리 AND로 결합 →0 0 13 * 5가 "13일 그리고 금요일"(=금요일의 13일만)으로 동작, 표준(Vixie/POSIX crontab(5): 둘 다 restricted면 OR = "13일 또는 임의 금요일")과 silent 불일치. cron-literate 유저가 기대하는 동작 아님(테스트 0개·DECISIONS 기록 0 = deliberate product 결정 아닌 단순화). 전 클래스 수정: ParsedCron이daysOfMonthRestricted/daysOfWeekRestricted(각 day 토큰이*였는지,*확장 전 parse-time 복원) 기록 + 공유cronMatchesDay(cron,dom,dow)(둘 다 restricted→OR, 아니면 단일 restricted 필드로 축약하는 AND) 신설 → nextCronTick·matchesCronMinute 둘 다 경유, plugin-job-scheduler는 nextCronTick 호출로 자동 상속. proof: 신설server/src/__tests__/cron.test.ts11/11(OR/AND 매트릭스 + 회귀 가드: 2026-03-21 토요일 기점0 0 13 * 5가 이제 다음 금요일 Mar 27=7일내, 구 AND라면 다음 금-13일 Nov 13=~238일로 점프) · server typecheck 0 · routines-service 33/33 · e2e 10/10. 누적 167 슬라이스. 세션 헌트 9종(연속 3 zero) — 표준-준수 갭 1건까지 수정.
자율(unattended) 모드 — 유저 참여 게이트 skip (WC-168~171, 사용자 goal: "간단한 프로젝트로 워크플로 밟아가며 QA + skip mode 만들고 테스트")
- 목표: 멀티에이전트 워크플로(오케스트레이터→디자이너[Open Design]→유저 검토→디벨로퍼→QA→컴파운드)를 실제 구동해 동작 확인 + 유저가 자기 참여를 건너뛰고 무제한 자율 실행할 "skip mode" 신설·테스트.
- WC-168(
35ca0bb) — 서버 autonomous mode: instance-level experimental flagautonomousMode. ON이면 (a) execution-policy 스테이지 중 참여자가 전부 user인 스테이지를 자동 승인(사람에 파킹 안 함) (b) 신규 에이전트 채용이 board-approval 게이트 우회. 의도적 미skip: agent review/QA/compound(자율 품질 루프) + 예산 안전게이트. 설계: shared experimental schema/type/normalizer에autonomousMode(기본 false, 마이그레이션 불요 — 기존 instance_settings 재사용). 엔진(issue-execution-policy.ts): 기존 self-review 자동-skip 메커니즘을 user-only 스테이지까지 확장(workflow-start 경로의 while-loop + post-approval next-stage 경로 양쪽), 신규skipUserParticipationtransition input으로 게이트 — flag OFF 시 byte-동일(loop guard가 false).stageIsUserOnlyexport. routes/issues.ts가 flag thread하되 정책에 user-only 스테이지가 실제 있을 때만 instance-settings read(핫 PATCH 경로 공통 케이스 오버헤드 0). routes/agents.ts 채용 게이트 2곳도 flag 반영. proof: 엔진 단위테스트 +5(시작/승인후 user-skip·agent QA는 미skip·flag OFF는 파킹)=55/55·route 테스트(mock seam)·typecheck·e2e 10/10. - WC-169(
c3516db) — UI 토글: Experimental 인스턴스 설정 페이지에 Autonomous Mode 토글(기존 experimental query/mutation 재사용, 신규 API 불요) + en/ko 로케일(무엇을 하고 무엇을 보존하는지 명시). ui typecheck·locale-validation 7/7. - WC-170(
ba6e2da) — 워크플로 실구동 e2e: 실제 제품(board+engineer+qa 에이전트)에 "Notion-lite" 프로젝트를 autonomous ON으로 구동 → 3/3: (1) user 디자인-검토 스테이지 자동 승인(사람 파킹 0·완료) (2) user 검토 skip→agent QA 스테이지 파킹→QA 완료 (3) agent QA는 미skip(품질 루프 유지). afterAll서 flag OFF 복원(인스턴스-글로벌 누수 방지; user-only 스테이지는 타 e2e 미사용이라 간섭 불가도 확인). 머지게이트=full e2e 13/13. - ✅ WC-171(
b219ae6) — QA가 발견한 실버그: 에이전트 삭제 race FK 위반. WC-170 e2e가 실행 중 런을 가진 에이전트 삭제 시 Postgres FK 위반(heartbeat_run_events_run_id_heartbeat_runs_id_fk)→전체 agent 삭제 롤백→런 진행 중 에이전트 삭제 불가를 노출. 근본: agentService.remove가 런 삭제 전에 run_events를 purge(WC-124)하나 그 purge가 live 런 executor와 경쟁 — purge 후·런삭제 전에 in-flight 런에 이벤트가 써지면 참조행 재유입→런삭제 FK 실패. fix:heartbeat_run_events.run_idON DELETE CASCADE(migration 0106). run_events는 런 소유 ephemeral 텔레메트리라 cascade가 정확한 소유모델 + 런 삭제와 원자적 제거→경쟁 무관 차단. agent_id FK(페어 카운터파트 런의 이벤트)는 여전히 명시 purge 유지(WC-124 주석 갱신). proof: cleanup-removal 15/15(신규 WC-171 테스트: 런 삭제가 이벤트 cascade)·migration embedded-pg+실서버부팅 적용·typecheck·full e2e 13/13(에이전트 정리서 FK 위반 소멸). ⚠교훈: 명시 purge가 있어도 동시 writer가 FK 참조를 재유입할 수 있다 → 자식 텔레메트리는 cascade가 race-free 정답(delete-path-fk 노트에 race 변종 추가). - WC-172(
8b211e7) — 컴파운드 리뷰 클린업: WC-168~171 적대적 컴파운드 리뷰(사용자 워크플로의 5번째 에이전트 역할) → 실버그 0(엔진 flag-off byte-동일·gate 건전성 확인) + 데드코드 2건·MED 문서갭 1건·HIGH 미완료-fix 1건 적발. 정리: agents.ts run_events purge의runId in agentRunsdisjunct(+agentRunIdsSql CTE)는 cascade로 dead→agentId-only로 축소; companies.tscompanyRunIds셀렉트+by-runId 삭제는 companyId purge로 이미 dead→제거; instance validator에 autonomousMode가 인스턴스-전역(전 회사 board-approval 완화 + agents:create 보유 에이전트가 채용 self-serve)임을 명문화. cleanup-removal 15/15·typecheck. - 통합 QA 헌트(워크플로 라이프사이클 × autonomous mode) = 실버그 0: skipped-stage 소비자(
issue_execution_decisions는 write-only·미SELECT,lastDecisionOutcome:null은 completed서 미read)·monitor×skip(done→monitor clear 이중방어: 트랜지션 clear + 스케줄러가 in_progress/in_review만 쿼리)·auto-complete 이슈 dispatch/lock(done이 lock 클리어·becameDone만 발화)·compound-checklist(done 게이트는 decision 무관·발화)·executionStateVersion OCC(전 multi-stage skip이 in-memory 1 UPDATE→guard==trigger 동일값·double-bump 0)·mixed-stage 전수 추적 후 sound. honest zero(컴파운드 리뷰된 코드라 타당). mixed [user,agent] 스테이지가 순서에 따라 user 파킹 가능은 문서화된 의도(다운스트림이 "autonomous는 절대 user 파킹 안 함"을 가정하는 코드 0 확인 — 비-corruption). - ✅ WC-174(
4d1aefd) — activity_log 동일 race 해결(WC-171 sibling, chip supersede): 컴파운드 리뷰가 WC-171 fix 미완료 적발 —activity_log의 run_id·agent_id 둘 다 동일 purge-vs-live-writer race. 처음엔 chip으로 추적(append-only 트리거 0101 때문에 비자명: SET NULL=UPDATE 차단·CASCADE=teardown blast-radius)했으나, SET NULL은 blast-radius 0(런 삭제가 "FK-block→pointer-null"로만 바뀜·기존 통과 teardown 불파괴)임을 확인 → 타깃 검증으로 안전하게 inline fix. migration 0107: run_id·agent_id ON DELETE SET NULL(삭제 시 audit 행 보존·dead pointer만 null; CASCADE는 audit 삭제라 부적합) + append-only 트리거가 FK-driven null-ing UPDATE만 정확히 허용(run_id/agent_id를 null로 + 그 외 전 컬럼 불변일 때만; 그 외 UPDATE·ad-hoc DELETE는 여전히 거부 → append-only 보존). proof: cleanup-removal 16/16(신규 WC-174: 런 삭제가 run_id null·audit 보존)·wc29 immutability 4/4(tampering 여전히 거부)·typecheck·e2e 13/13(0107 실서버부팅 적용·agent/company 정리서 activity_log FK 위반 0). ⇒ agent-delete FK race(run_events·activity_log) 종결. + WC-175(bdba587): 마지막 동류agent_task_sessions.last_run_id도 SET NULL(migration 0108, 트리거 없음→trivial; cleanup-removal 16/16). ⇒ agent-delete의 heartbeat_runs 자식 FK race 전(全) 클래스 종결(run_events cascade·activity_log+이것 SET NULL·watchdog cascade·cost/finance SET NULL). 컴파운드 리뷰 발견 전건(클린업 WC-172·mixed-stage WC-173·activity_log race WC-174·sibling WC-175) 100% 해소.
- CLI 가용 확인(이전 "로그인 CLI 필요" 주장 정정):
claude.exe(C:\Users\solom.local\bin, v2.1.139 Claude Code, 인증됨)·codex둘 다 설치+인증됨. 헤드리스claude --print -동작 확인(PONG 반환). ⇒ claude_local 어댑터로 실 LLM 에이전트 run 구동 가능(이전 "stub만 가능"은 틀렸음). - 실 구동(격리 throwaway 인스턴스 port 3210, 별도 home, claude_local 오케스트레이터 에이전트): 오케스트레이터(실 opus-4-7)가 "Notion-lite" 기획 이슈를 end-to-end 처리 — Workcell API로 자기 task 읽고, 실제 plan 문서(4.3KB) 생성, 코멘트 작성, 이슈를 in_review로 이동 + request_confirmation(유저 검토 게이트) 생성. run 성공(exit 0·~$1.4·opus-4-7). cwd=throwaway 워크스페이스(레포 미오염, 커밋된 트리). ⇒ 오케스트레이터 단계가 실 에이전트로 동작함을 실증.
- 🔴 실버그 발견(실 워크플로 구동이 노출): skip mode가 실 user-review 게이트 미커버. WC-168 skip mode는 executionPolicy user-stage를 auto-approve하나, 실 에이전트는 유저 검토를 request_confirmation thread-interaction(다른 메커니즘)으로 게이트함(
executionPolicy=null). ⇒ skip mode ON이어도 실 에이전트 plan이 request_confirmation에 여전히 파킹(무제한 실행 목표 위배). stub e2e(executionPolicy)는 통과했으나 실 워크플로가 갭 노출 — 정확히 이 라이브 구동이 잡으려던 것. - ✅ WC-176(
82e657f+테스트c84d6b0) — autonomous mode가 request_confirmation도 auto-accept: POST /issues/:id/interactions에서 autonomous ON + request_confirmation 생성 시 system으로 auto-accept + 생성 에이전트 재wake(/accept 라우트의 continuation 처리 미러; plan confirmation은 forceFreshSession). user 게이트만 skip, 에이전트 작업은 계속. proof(라이브, 동일 인스턴스에 재배포): autonomous OFF=plan이 request_confirmationpending·이슈in_review고착(NOT-1); autonomous ON(이 fix)=request_confirmationaccepted·continuation run 발화·이슈done무인 도달(NOT-2). + route 테스트 12/12(ON=auto-accept+wake / OFF=pending)·typecheck·full e2e 13/13. ⇒ skip mode가 실 에이전트 워크플로의 실 user 게이트까지 커버 — 무제한 무인 실행 목표 달성. ⚠️ follow-up(비치명적): heartbeat_run_events insert deadlock. full e2e서 성공한 run의 최종 이벤트 insert가 1회deadlock detected(테스트는 13/13 통과). WC-171 cascade가 delete-side 락 패턴을 바꿔 agent-delete-mid-run race가 FK위반→deadlock으로 형태 변경됐을 가능성(insert-side FK락은 불변이라 추정). 비치명적·타이밍 의존. 재현 잦으면 조사(별도).- 실 멀티에이전트 워크플로 전(全) 단계 라이브 구동(2차 throwaway 인스턴스, autonomous ON, 공유 cwd): 사용자의 5-에이전트 워크플로를 실 LLM 에이전트로 end-to-end 구동·확인:
- 디벨로퍼(실 opus, engineer): 코딩 이슈("Notion-lite note card") 수령→실제 작동 코드
note-card.html(7.8KB·312줄, 정품 Notion 팔레트/섀도/타이포) 작성+proof work-product 첨부+done 전이→executionPolicy review 스테이지가 QA 에이전트로 라우팅(in_review·returnAssignee=dev). 비용 $1.52. - QA 리뷰어(실 opus, qa): 실제로 note-card.html(312줄)을 읽고 이슈 요구사항 전수 검증(단일파일·무의존/무빌드·contenteditable title/body·카드 스타일 —
<link>/CDN 0개·IIFE 스코프 확인)+품질 디테일 포착(debounced autosave·plain-text paste)→승인→이슈 done(executionState completed·outcome approved). 비용 $0.46. 러버스탬프 아닌 실 리뷰. - 컴파운드: done 전이 시 compound-checklist 문서 자동 생성(WC-12, 5섹션 템플릿) — 컴파운드 리뷰어의 작업 표면.
- ⇒ 오케스트레이터(plan)·디벨로퍼(실 코드)·QA(실 리뷰)·컴파운드(체크리스트)·skip mode(user 게이트) 전부 실 에이전트로 실증. 디자이너의 실 Open Design 시안만 OD 데몬(외부) 필요 — 디자인-리뷰 게이트 자체는 request_confirmation으로 커버됨.
- 디벨로퍼(실 opus, engineer): 코딩 이슈("Notion-lite note card") 수령→실제 작동 코드
- 누적 176 슬라이스. ⇒ 사용자 goal 완전 달성: skip mode 빌드+테스트(엔진 57·route·e2e 13/13) 완료, 실 LLM 에이전트로 전 워크플로(오케스트레이터→디벨로퍼→QA→done→컴파운드) 실구동 성공, 실 구동이 skip mode 갭(request_confirmation 미커버) 노출→WC-176 수정·라이브 재검증(OFF=stuck·ON=done). CLI 가용성 정정(claude.exe 인증됨). throwaway 인스턴스 2개 정리.
- WC-173(
624b424) — mixed user+agent 스테이지를 agent로 라우팅(컴파운드+헌트 둘 다 flag한 gap 종결): user+agent 둘 다 있는 스테이지는 user-only가 아니라 auto-skip 안 되는데, selectStageParticipant가 첫 비-제외 참여자(순서상 user면 user)를 골라 autonomous인데도 사람에 파킹 가능 → "skip user participation" 약속 위반. fix:selectStageParticipant에 additivepreferAgent옵션(explicit preferred는 여전히 우선; 임의 first-pick만 변경) + 3개 forward 파킹 셀렉션(start/skip-loop/post-approval)에preferAgent: skipUserParticipation. mixed 스테이지가 이제 agent에 파킹→자율 품질 루프로 계속(사람 미대기). flag OFF=동일. proof: 엔진 57/57(+2: user-first mixed가 autonomous서 agent 파킹·OFF서 user 파킹)·typecheck·e2e 13/13. - 누적 173 슬라이스. ✅ skip mode 기능 완결: 빌드(서버 WC-168+UI WC-169)·단위(엔진 57)·route·e2e(autonomous 3 + full 13/13)·통합QA헌트(0버그)·컴파운드리뷰(클린업 WC-172)·mixed-stage gap 종결(WC-173). QA가 실버그 1건(FK race WC-171) 발견·수정, 1건(activity_log sibling race) 추적 chip. 남은 것=외부 시스템 의존(실 LLM 에이전트 작업=로그인 CLI; 실 Open Design 아티팩트=OD 데몬) 또는 추적된 chip(activity_log race). e2e stub adapter는 구조적 워크플로·스테이지 전이·skip mode를 전부 커버; 실 LLM 에이전트 산출물만 미커버.
- 삭제 핫존 종결 후, 세션 누적 서비스 변경(삭제 WC-134/135 + 페어 WC-125~133)을 cohesive 통합 cert로 교차 검증("per-slice green ≠ integration green" 교훈 적용) → WC-29 트리거가 도입한 광범위 잠복 red 발견.
- WC-136(
e005ae6) — activity_log teardown 클래스 수정(22 스위트): WC-29 append-only 트리거(activity_log_block_mutation, row-level DELETE 차단)가 도입됐으나 22개 테스트 스위트가 여전히db.delete(activityLog)로 teardown → activity 행을 seed하는 스위트마다 afterEach에서activity_log is append-only — DELETE rejected500. WC-29 시점부터 56개 테스트 / 21개 스위트가 잠복 red였으나 per-slice 머지게이트가 각 슬라이스 자기 스위트만 돌려 한 번도 재실행 안 돼 안 보였음(participation/inbox/liveness/recovery/routine 흐름). 수정(WC-50 wc13 선례):db.delete(activityLog)→TRUNCATE … RESTART IDENTITY CASCADE(DDL이 row trigger 우회), 29개소/22파일 + 미사용activityLogimport 6개 제거.wc29-activity-log-immutability는 여전히 DELETE 거부 단언(트리거 자체 불변=immutability 보존). proof: server typecheck 0 · 23 스위트/271 테스트 green(2배치 — 23 동시는 embedded-pg 자원 경합 위양성이라 ~12개씩 분할). 솔루션 노트 신설docs/solutions/activity-log-teardown-truncate.md(+INDEX).⚠️ 교훈: 전역 DB 변경(트리거/제약) 직후엔 그 테이블을 teardown하는 전 스위트를 grep+cohesive cert로 전수 검증 — per-slice 타깃 그린은 통합 그린이 아니다(WC-56·WC-73과 동류, 이번엔 트리거 클래스로 21스위트 규모).
- 서버 적대적 버그헌트(서브에이전트, 독립 재검증)로 HIGH 심각도 cross-tenant 읽기 누수 1건 발견·수정. 나머지 서비스(pair·cost·quota·dispatch·capabilities·secrets·KG)는 견고 확인.
- WC-112(
6e1cfc6) —getAncestorscross-tenant 누수:issues.parentId(self-FK, 동일회사 제약 없음)에 회사 A 멤버가 회사 B 이슈 UUID를 PATCH/POST(검증=UUID 형식만, create 가드는inheritExecutionWorkspaceFromIssueId로 우회, update 무가드)로 저장 가능 →getAncestors가 부모 체인을 companyId 술어 없이 순회해 B 이슈의 title/description/assignee/project/goal을 issue-context 응답으로 반환. 수정(WC-54 패턴): 순회를 start 이슈의 companyId로 스코프(and(eq(companyId), eq(id))+ 루프 가드) → cross-company hop은 null로 끊김, 누수 0(하위 project/goal fetch도 raw에 안 들어가 안전). defense-in-depth(쓰기-시점 parentId 가드)=WC-138(5b196bb)로 해소:assertParentIssueInCompany로 create(항상 검증 — inheritExecutionWorkspaceFromIssueId 우회 차단)+update(set/change 시 검증). cross-tenant parentId 저장 자체를 차단. wc112 6/6(+3 write-guard, update-reject=genuine red-first)·issue 회귀 58/58·e2e 9/9. 추가 WC-140(9602959): 같은 tenant-isolation 불변식을 projectId/goalId로 확장(WC-138 sibling) — 이슈가 타사 project/goal 참조 불가. 콘텐츠 누수는 없음(이슈 서비스가 project/goal을 read에서 join 안 함, 파생 lookup은 이미 company-scoped) → parentId(WC-112=실 read 누수)와 달리 inert하나, 멀티테넌트 불변식(이슈는 한 회사에 온전히 귀속) 위반이라 일관성+격리 강화. create/update guard. wc112 9/9·issue 회귀 73/73·e2e 9/9. +삭제-캐스케이드 잔여 엔터티 전수 감사 완료(environments=자식 FK onDelete set null/cascade·secrets=소프트삭제·work-products=leaf FK 자식 0 → 전부 안전; company/agent/issue/project/goal은 기존 수정 완료) ⇒ 삭제 버그 클래스 전 엔터티 확정 종료, 신규 0. - 검증: 서버
tsc --noEmit0 +wc1123/3 green(cross-company 미순회·동일회사 정상·혼합체인 중단) — 세션 후반 환경 회복 후 실행 확인(초반엔 embedded-pg 부하로 skip됐으나 부하 빠진 뒤 green). WC-54와 동형 1-술어 스코핑.
- UI/shared 버그헌트(~60파일 정독) = 실버그 0(코드베이스 견고 확인). adapter-utils/cli/plugin-sdk 헌트 = 실버그 1건 발견·수정.
- WC-113(
6eb5fe0) — Codex config.toml 제어문자 이스케이프:tomlString이\·"만 이스케이프(제어문자 누락) → MCP 서버 name/command/arg/env 값에 개행/탭 등이 있으면 raw 제어문자가 TOML basic string에 들어가 invalid TOML → CODEX_HOME/config.toml 전체 unparseable(그 run에서 Codex가 설정 전부 못 읽음).agentConfig.mcpServers에서 미검증 도달(WC-68류 serializer 비대칭 —.mcp.json은 JSON.stringify라 안전). 수정: 제어문자(U+0000–U+001F·U+007F)를\b\t\n\f\r+\uXXXX로 이스케이프, 일반문자(공백/하이픈 포함)는 verbatim(회귀 테스트 — over-broad 이스케이프가 "open-design" 깨뜨리는 것 방지). proof: adapter-utils mcp-servers 11/11(+2 WC-113)·typecheck 0. - WC-114(
66f2c75) — README TODO/Roadmap 헤딩 깊이:extractTodosFromReadme가##?(H1/H2만)로 섹션 경계 판정 →### Subsection이 TODO 섹션을 안 닫아 그 bullet들이 잘못 suggested issue로 잡힘.#{1,6}로 수정. cli bootstrap-scanner 7/7. (저심각 —--dry-run사람 검토용.) - follow-up(BACKLOG/저위험): codex-local dedupe
existing.includes('mcp_servers."${name}"')substring 체크가"포함 name에 취약 — 사소.
- 세션 후반 호스트의
@playwright/mcp프로세스 40+ 누수가 CPU/IO를 포화시켜 한때 embedded-Postgres support timeout → DB 스위트 skip + e2e 부팅 초과. 이후 부하가 빠지며 회복 — wc112/wc64 등 DB 스위트가 다시 green. WC-111(e2e webServer timeout env-override)은 부하 상황 대비로 유지. 교훈: Windows 임베디드-pg/playwright는 누수 프로세스로 일시적 위양성 가능 → DB-독립(UI jsdom·lib 단위)은 항상 신뢰, DB/e2e는 부하 빠진 뒤 재실행. 잔여 누수 프로세스 정리는 하니스 재시작이 가장 깨끗(시스템 Postgres·하니스 MCP는 미터치).
- (없음 —
develop최신. i18n #6 확장 진행(WC-82~83: 메커니즘+계정메뉴+사이드바 한국어) + 리얼리티체크 6-point(WC-7781) + 정체성 arc(WC-7075) + Phase 1+2 누적.) - 완료 슬라이스(30): WC-2~WC-22 Phase 1 + D19 + Usage Center + UX 루프(자세히=memory). WC-23~WC-26 = WorkOwner pair substrate 완성(PLAN §9 #3 🔲→🟡). WC-27/30 = Capability Registry substrate + HTTP 라우트(PLAN §9 #7 🔲→🟡). WC-28 = D12 Knowledge Graph PoC substrate. WC-29 = activity_log 불변 강제(PLAN §9 #10 🟡→✅, DB trigger).
- WC-23 issues.work_owner_kind 컬럼 + ISSUE_WORK_OWNER_KINDS + Issue.workOwnerKind expose.
- WC-24 PairGroup data model(pair_groups 테이블 + Issue.pairGroupId back-ref + pairGroupService CRUD + create()가 issue.workOwnerKind를 atomically pair로 flip + getActiveForIssue + transitionStatus).
- WC-25 PairTurn ledger(pair_turns 테이블 unique(group,round,actor) + recordTurn/advanceRound/listTurnsForGroup) + auto-stop 정책 평가(abort 즉시/max-rounds 초과/requireConvergence+counterpart no_change → 자동 status 전환).
- WC-26 REST 라우트(GET /issues/:id/pair-group, POST 생성 with 409 on duplicate active, GET/POST /pair-groups/:id/turns, POST /advance, PATCH /:id). 모두 parameterized Drizzle lookup으로 회사 스코핑.
main(릴리스, 미사용) /develop(통합·최신·현재 체크아웃) /issue/*(보존).- develop 헤드 = WC-117(
d7d3a97). 누적: …(중략) + WC-106(라우트 에러 바운더리) + WC-107~110(Graphify 코드그래프 자율 슬라이스) + WC-111(e2e timeout env-override) + 버그헌트 수정 WC-112(getAncestors cross-tenant 누수 보안)·WC-113(Codex TOML 제어문자)·WC-114(README 헤딩)·WC-115(codex dedupe 키 통일)·WC-116(activity_log 트리거 vs 엔터티 삭제 — masked red→green)·WC-117+WC-118(회사삭제 orphan purge — Finding 2 완결, cleanup-removal 5/5)·WC-119(OD 대시보드 버전 diff 역전 수정)·WC-120(OD compare 로직 추출+테스트 하드닝) + WC-121(Graphify export→CodeGraphImport 매퍼, 실 graphifyy 0.8.28 대조) + WC-122(Graphify 프로듀서 CLI + ingest-graphify 라우트 — D20 end-to-end 완성) + WC-123(KG 뷰어 패널 — D12 S5, 첫 user-visible KG 표면) + WC-124(에이전트 삭제 캐스케이드 버그 수정 — 6번째 헌트, e2e 로그 발굴). 버그헌트 5회(server·UI/shared·adapter-utils/cli/plugin-sdk·db/bridge/runtime·adapter-execute/plugin-examples) = 전 코드 표면 커버, 실버그 8건 수정(2 HIGH). 알려진 미해결 버그 0. develop 헤드 = WC-166(a630694) — 페어 우선(WC-145150) + 첫실행 QA(WC-151153) + 코어엔진 헌트 실버그 3건(WC-154 budget race·WC-155/156 스케줄러 starvation) + 삭제경로·보안 후속 헌트 실버그 3건(WC-157 cloud-upstreams IDOR·WC-158 orphan 루틴 pause·WC-159 삭제 FK 완전성 2 HIGH) + tx 원자성 WC-160(project goal-link sync, 2 site) + lost-update WC-161(instance-settings atomic jsonb merge) + 입력검증 WC-162(cost/goal FK 존재검증, 500→4xx, 2 site) + executionState lost-update WC-163(version-column OCC — v1 raw-jsonb은 e2e veto→revert, v2 정수 version 성공) + 통합 cert(e2e 10/10). 대시보드/실패가시성/approvals/server-percent QA clean. (이전: WC-141144 final-wave + 문서 · WC-136140 activity_log teardown·billing 보존·cross-tenant write-guard.) GitHub push 전 squash 예정(원격 미존재 — 사용자 결정 필요).⚠️ WC-84+ 슬라이스는 issue 브랜치 없이 develop 직접 커밋(각 typecheck+타깃 스위트 green; e2e는 클러스터 단위).
- 재검증 워크플로
wslzdl135(7 에이전트, 2026-05-29): 150/150 green — 서버 영향 스위트 78(compound+capability 32 · pair 24 · graph+dispatch+design 22) + UI 63 + e2e 9. WC-50~56 16 findings + 1 회귀 전부 closed. - WC-96~105 루프 인증(2026-06-01): 전체 워크스페이스
pnpm typecheck0(19 프로젝트) · UI vitest 1114/1114(177 files) · server pair 29/29 ·pnpm test:e2e9/9. 10 슬라이스(온보딩 네이티브·발견성·pair 모델/워크스페이스 parity·CompanySettings·agent-config·toast·JSX텍스트 i18n) 모든 레이어 green. - WC-106 인증(2026-06-02): 전체 워크스페이스 typecheck 0(19) · UI vitest 1120/1120(178 files; +ErrorBoundary 6) · e2e 9/9. 라우트/루트 에러 바운더리 — Layout
<Outlet/>wrap 무회귀(Layout 8/8), 해피패스 passthrough. - WC-107
116 인증(2026-06-02, 환경 회복 후 green): typecheck 0(19) · KG wc28 29/29 · wc64 5/5 · wc110 7/7 · wc112 3/3(보안) · adapter-utils mcp-servers 12/12 · cli bootstrap-scanner 7/7 ·114) · wc29 4/4 + cleanup-removal-service green(WC-116, masked red→green). Graphify 자율 클러스터(WC-107~110) + e2e timeout override(WC-111) + 버그헌트 4회(server·UI/shared·adapter/cli/plugin·db/bridge/runtime) → 실버그 수정 5건(WC-112 보안 cross-tenant·WC-113 TOML·WC-114 README·WC-115 codex dedupe·WC-116 삭제 vs 트리거) 인증. (세션 중pnpm test:e2e9/9(WC-107@playwright/mcp누수로 DB/e2e 일시 위양성이었으나 부하 회복 후 재실행 green.)⚠️ WC-116 = security-sensitive(append-only 트리거에 GUC-gated DELETE 예외 추가 — UPDATE·ad-hoc DELETE는 여전히 차단; 커밋 메시지에 리뷰 노트). 버그헌트 Finding 2(companyService.remove 캐스케이드 누락) = WC-117/118로 완결(전수 FK 감사 → true orphan 10개 purge, 나머지는 부모 cascade 확인; cleanup-removal 5/5). - WC-136 cohesive cert(2026-06-02): activity_log teardown 클래스 23 스위트/271 테스트 green(신뢰 small-batch ~12개씩 — 23 동시는 embedded-pg 자원경합 위양성). server typecheck 0. broad
--mode general --group general-servergiant-batch도 실행 → 68 fail이나 전수 triage = 코드회귀 0: 전부 ⓐ Windows-spawn env(claude-local-execute fakeclaude·workspace-runtime/heartbeat-local-environment/environment-runtimespawn sh ENOENT/EFTYPE·qa-routine-secrets-e2e) ⓑ POSIX-path env(session-workspace-cwdisUnsafeSessionWorkspaceCwd("/")Windows서 false — Linux/CI green) ⓒ embedded-pg 거대배치 contention(의심 DB-service 12스위트 + secrets-service 단독 전부 green; cleanup-removal도 단독 10/10인데 배치선 fail). →⚠️ giant-batch는 Windows 호스트서 untrusted 신호 — stable-runner serialized(--mode serialized, 스위트당 1 invocation isolate) 또는 ~12개 small-batch가 정답. WC-29류 전역 DB변경 후엔 cohesive cert 필수(WC-136이 21스위트 잠복 red 적발). - e2e 9/9 green (
pnpm test:e2e— onboarding·proof-gated-done·signoff-policy·planning-visual·compound-flow 등; WC-96?welcome=1온보딩 네비 변경 무회귀 확인). - 서버 vitest는 전체 배치가 임베디드-pg+vi.mock 교차오염으로 flaky → 영향 스위트 개별 실행 정책. WC-suite + 인접(issues/documents/activity) green.
- ✅ Open Design = source of truth (D22) — (a)+(b) 전 슬라이스 완료 (2026-06-04, develop 헤드
9ac6c1e): dogfooding WC-177~180 + 메모리그래프 WC-181 + WC-182(파운데이션·게이트 API·게이트 UI·(a)① 생성/첨부·② 게이트→개발 컨텍스트주입+hold·③ 디자이너 에이전트 MCP툴 design_attach/submit) + WC-183((b)1extractDesignSystem+POST /issues/:id/design-system·2workcell design scanCLI·3IssueDesignComparePanel복각 대조) 전부 머지. 검증: 슬라이스별 타깃 그린 + 풀 UI 1163/1163 + (a)-arc 서버통합 47/47 + 화면검증 2건(게이트 미제출→보드검토→승인됨 / 복각 디자인시스템-대조, charset=utf-8 한국어 클린). 상세=DECISIONS D22 구현현황. 후속(의도적 비범위): 하드 스케줄러 차단, 실 헤드리스 스캔 런타임. - ✅ 디자인 source-of-truth 강화 + 도그푸딩 환류 (WC-184~198, 2026-06-05, develop 헤드
009f483): forge-party 도그푸딩에서 발굴한 제품 갭을 Workcell로 환류. CP 배치: WC-184 grill-mode 이슈생성 토글 · WC-185 pair를 "누가 일하나" 전 표면에서 1급 옵션화 · WC-186 에이전트 커스텀-인스트럭션 인라인 에디터 · WC-187 design-first 게이트(시안 승인 전 dev 완료 차단) · WC-188 plan 피드백→플래너 수정 루프 · WC-189 pair-bound 에이전트를 에이전트 목록/조직도에 노출 · WC-190 조직도 뷰포트 리사이즈 auto-fit(도그푸딩 버그). 디자인 시스템 source-of-truth 창(사용자 요청 3건): WC-191 Design System 사이드바 페이지(상시 접근) · WC-192 디자인 버전 deprecation 라이프사이클+AI auto-prune · WC-193/design-system라우팅 수정(board route root+redirect) · WC-194 superseded 디자인 하드 삭제(되돌리기 불가, 사용자 선택)+산출물 current-design 카탈로그(화면당 1 authoritative) · WC-195 design-first 게이트=회사 opt-in(companies.require_design_first)+이슈별 예외(blast radius 0) · WC-196 이슈별 design-exempt 토글(AI도setByKind:"auto"로 예외처리) · WC-197IssueDesignRequirement공유 타입 정식화 · WC-198 회사-레벨 design-first 토글 in-app(CompanySettings). 검증: wc187-design-gated-done 7/7 + work-products 22/22 + companies API read/write 라운드트립 + CompanySettings 12/12 + 전 패키지 tsc 0 + migration 0110(design_requirement·require_design_first). 각--no-ff머지. 누적 ~198 슬라이스. - ✅ WC-199 design-first 게이트 하드닝 (2026-06-05, develop
7874df5): WC-184~198 아크를 적대적 리뷰(서브에이전트 2종, server+UI)로 재검증 → 실결함 5건 수정. [HIGH/보안]PATCH /work-products/:id가 design 타입의reviewState/isPrimary를 수용 → 이슈 담당 에이전트가 자기 시안을 self-approve해 board-only 승인 + design-first Done 게이트를 우회 가능했음 → 422(design_review_route_required)로 거부, 거버넌스 라우트(/design-review/*)로 강제(isDesignWorkProductType가드). [MED] design 게이트가 proof 게이트의 면제(issueRequiresProofForDone)를 공유하지 않아 planning/recovery(비-화면) 이슈가 opt-in 회사에서 잘못 차단 → early(route)+late(svc) 게이트 둘 다 면제 공유. [MED] DesignSystem 산출물lineageKey가 끝자리 숫자만으로 머지 → 별개 화면("Dashboard 2024/2025")을 한 lineage로 합쳐 디자인을 숨김(사용자 "모든 디자인이 보여야" 위반) → 명시적v버전 토큰 요구로 수정. [MED] IssueDesignReviewPanel 뮤테이션이 canonicalissues.detail캐시를 무효화 안 해 토글된 요구사항이 공유 Issue에 stale → 무효화 추가. [LOW]POST /issues/:id/design-requirementzod 바디 검증 추가. proof: design-review-routes 14/14(+2 보안)·wc187 8/8(+1 planning)·UI IssueDesignReviewPanel 15/15·lineageKey 회귀 인라인 검증(v-머지/숫자-분리)·server+ui typecheck 0·--no-ff머지(7874df5). 잔여 LOW follow-up(비차단, 기존 컨벤션과 동일): 다중버전 lineage 삭제 시 카드 잔존(UX)·company 토글 에러 피드백. 누적 ~199 슬라이스. - ✅ WC-200~201 자율 개선 증분 (2026-06-05, develop
d526c8a): WC-200 — CompanySettings 토글(hiring+design-first) pending/error 피드백(ToggleField에 optionaldisabled추가; 저장 중 비활성+실패 시 메시지 — 기존 silent-revert 수정, WC-199 LOW-3 해소). UI tc 0·CompanySettings 12/12. WC-201 — pair-collab 워크스페이스 머터리얼라이제이션(D21) 적대적 리뷰(서브에이전트). 보안 카테고리 전부 CLEAN(secret-to-disk·path-traversal·cross-tenant·JWT·flag-gating·cleanup tenant-safety) — 6 findings(1H·2M·3L)=correctness/leak: [HIGH]closePairWorktrees가 DB 컬럼만 flip하고cleanupEligibleAt스윕이 없어 live pair마다 worktree 디렉터리+브랜치 영구 누수 → 매뉴얼-close와 동일cleanupExecutionWorkspaceArtifacts호출(per-row best-effort). [MED] worktreecreatedByRuntime태그 누락→브랜치 미삭제 수정. [LOW] 재사용 cwdexistsSync가드 + 오해 소지 주석. proof: wc131 6/6(reap 테스트를 디렉터리 실제 삭제+createdByRuntime 단언으로 강화)·pair-workspace 2/2·server tc 0. deferred(spawn task): 동시 run-round double-insert(unique index/lock)·secret-name 로그 redaction. 누적 ~201 슬라이스. - ✅ WC-202~203 자율 개선 증분 (2026-06-05, develop
57d9f80): WC-202 — DesignSystem 산출물 카탈로그: 다중버전 lineage의 current 삭제 시 카드가 안 사라지고 다음 버전으로 폴백하는 게 'permanent delete'로서 혼란 → 'Nv' 버전 카운트 배지 + 삭제 confirm에 '다른 버전 N개 남아 카드 유지' 안내(WC-199 LOW-1 해소). UI tc 0. → WC-199 LOW 2건 모두 완료. WC-203 — CP 배치(WC-184~189) 적대적 리뷰(서브에이전트). 최우선 타깃 WC-188 plan-feedback 루프 = CLEAN(board-gated·non-mutating·injection-clean), WC-186 custom-instructions(board-only)·WC-189 pair list(company-filtered)·WC-184 grill 전부 clean. 1 MED 수정:POST /issues/:id/pair-group가 owner/counterpartAgentId를 company 검증 없이 바인딩(pair_groups FK가 agents를 company 제약 없이 참조)→다른 테넌트 에이전트를 UUID로 바인딩 가능(pre-existing WC-26, WC-185가 노출).pairGroupService.create가 insert 전 바인딩 agent의 company 소속 검증(아니면 badRequest 400). proof: wc26-pair-group-routes 8/8(+1 cross-tenant rejection)·server tc 0. deferred LOW(선택): plan-revision 코멘트 spam coalesce. 누적 ~203 슬라이스. - 🚧 WC-204~205 Deliberation 모드 (dual-brain 내부 합의) — flagship 재설계, 슬라이스 1·2/4 (2026-06-05, develop
a90402e): 사용자 피드백 = 기존 pair('서로 다른 두 에이전트 묶기')가 아니라 **한 에이전트 안 두 brain(2중인격)**이 내부에서 제안→검토(Accept/수정)→티키타카하다 한쪽 Accept 시 결론. 아무 역할이나 pair 모드 가능. 확정(AskUserQuestion): 두 모델 각각 지정 + 에이전트별 모드(pair 개념 대체). WC-204(엔진+데이터):agents.deliberationjsonb(migration 0111) + 공유AgentDeliberationConfig(brainA/brainB model + maxRounds) + validator +runAgentDeliberationinvoke-agnostic 합의 루프(A propose→reviewer B부터 교대→accept stop / revise swap → maxRounds cap; JSON verdict 방어 파싱) + 헤르메틱 5/5. WC-205(설정 UI): AgentConfigForm edit에 Deliberation 섹션(enabled 토글→brain A/B ModelDropdown + maxRounds) + create/updateAgentSchema에 deliberation 수용(DB컬럼·서비스는 이미 영속, validator만 갭). UI 4/4·patch-lib 8/8·route 3/3·전 typecheck 0·각--no-ff. 남은: 슬라이스 3(실행 배선 — deliberation ON 에이전트 턴을 루프로 라우팅)·4(A↔B 티키타카 타임라인 UI).⚠️ :3199는 0111 적용 위해 재부팅 필요. 누적 ~205 슬라이스. - 🚧 WC-206~207 Deliberation 실행 + 라이브 smoke test (슬라이스 3/4, 2026-06-05, develop
47e5db6): WC-206(라이브 라우트):POST /agents/:id/deliberate {task}→ 에이전트 brainA/brainB 모델로 실제 합의 실행 → transcript 반환. flag-gate(WORKCELL_PAIR_LIVE_LLM, OFF=503)·비용 activity 기록·테스트 6/6. 빡센 smoke test(라이브 :3199, FP 기획자, claude-opus-4-7): ① 첫 실행이 버그 노출 → WC-207 수정: 라이브 invoke가runAdapterSingleTurn의 raw stdout(claude-local NDJSON 스트림)을 반환 → brain들이 프로토콜 쓰레기를 보고 verdict 파서가 첫 system-hook JSON을 잡음 →result.summary(파싱된 어시스턴트 텍스트) 반환으로 수정 + 헤르메틱 3 테스트(9/9). ② 수정 후 재실행 클린 성공(90s): brain A 방치보상 공식+UI 초안 → brain B 검토·개선(min()캡, forge테마 '제련량', 시너지 ×1.4→×1.6, 메타코멘트 제거)해 ship 가능 형태로 — 진짜 dual-brain 티키타카 작동 확인. smoke test 추가 findings(follow-up): ⓐ 동기 라우트가 다라운드 라이브서5분 블로킹 → async(job+poll) 필요 ⓑ transcript가 HTTP 응답에만 → 타임아웃 시 결과 유실 → 영속화 필요 ⓒ brain 2개=한 어댑터 내 2모델(claude↔codex cross-adapter 아님) — 설계 확장 여지 ⓓ pair 라이브 invoker도 동일 raw-stdout 잠복버그. **남은: 슬라이스 4(티키타카 타임라인 UI) + 위 ⓐⓓ.** 누적 ~207 슬라이스. - 🚧 WC-208~ Deliberation 완성 (사용자 "A·B·C·D 전부 진행", C=brain별 adapter+model 유저선택 결정): WC-208(finding C, 머지
063d614): 각 brain이 adapter+model 독립 선택 — cross-adapter(brain A=claude_local, B=codex_local) 또는 같은 어댑터 2모델. brainConfig{adapter?, model?}(null=상속), 엔진이 adapter를 invoke→CLI로 스레딩, 라이브 invoke가 brain별 다른 CLI 해석, 설정 UI에 brain별 adapter 픽커. 마이그레이션 불요(jsonb). server 20+ui 12·tc 0. WC-209(A, 머지511d557): async+영속 —agent_deliberation_runs/_turns테이블 + migration 0112 +POST /deliberate=202{runId,status}(fire-and-forget) + 엔진onTurn증분 영속 + poll GET(list/run+turns) + per-turn 비용. engine 7+service 5+route 10+regression 8·tc 0. WC-210(B+D, 머지8882698): B=DeliberationPanel(AgentDetail 'Deliberation' 탭, enabled시 노출 — 실행 폼 + A↔B 티키타카 타임라인 2s poll: 턴별 brain/action/content/feedback/cost + finalOutput + acceptedBy/rounds/총비용 + 503 'live 비활성' 안내 + 과거 run 목록) · D=pair-turn-real-invoke도result.summary(WC-207 동일 수정). UI 4+pair 14+50 인접 green·tc 0. → ✅ Deliberation A·B·C·D 전부 완료(엔진 204·설정 205·라이브 206·clean 207·brain별 adapter+model[C]208·async+영속[A]209·타임라인 UI+pair[B/D]210).⚠️ :3199 migration 0112 적용 재부팅함. 누적 ~210 슬라이스. - ✅ 게임 기획/제작 라이브 smoke test + WC-211 하드닝 (2026-06-06, develop
9820692): 라이브 :3199(WORKCELL_PAIR_LIVE_LLM=1)서 dogfooding 흐름 검증 — 디자인-우선 게이트 PASS(디자인 없이 done→409 design_review_pending / 승인 후 통과) · 기획(planner draft) PASS(heartbeat 자동 dispatch→오케스트레이터 4 runs 실행→고품질 4섹션 초안 문서[AC 8·Non-Goals 8, 디자인 토큰 강제] = execution 루프도 암묵 증명; "JWT missing"이 claude_local 실행 안 막음) · deliberation async+영속 PASS(202 즉시 + 턴 증분 영속 + 내 클라 240s 타임아웃에도 run 생존, WC-209 fix 라이브 검증) · cross-adapter(Claude+Codex) PARTIAL(brain A=Claude 정상·영속, brain B=Codex ~5분 매우 느림—행 아니라 결국 completed). smoke 3 findings → WC-211(머지9820692): ① deliberation per-turn 타임아웃(DELIBERATION_TURN_TIMEOUT_MS=120s; adaptertimeoutSecknob가 CLI kill + Promise.race 백스톱; 타임아웃→throw→run 'failed' graceful) ② planner draft title 70자 word-boundary 정제(원문은 instruction 유지) ③reapStaleDeliberationRuns(20분+ 'running'→'failed', 서버 startup 무조건 호출). 39/39 + 인접 10/10·tc 0.⚠️ 열린 follow-up(칩task_b772e9cd): Codex 턴이 ~5분이라 120s 타임아웃이 너무 짧아 cross-adapter-Codex가 매 턴 타임아웃될 수 있음 → 측정 후 타임아웃 상향(360s)/어댑터별/에이전트별 튜닝 필요. (저가치: db buildcp -r비멱등.) 누적 ~211 슬라이스. - 사용자 입력/결정 필요(자율 불가): GitHub push 전 squash(원격 미존재 + org/repo/공개여부 결정) · light 톤 soften 여부.
- 코드 그래프화 = Graphify 외부 MCP 사이드카(D20): ✅ S1
S4 코드 end-to-end 완성(WC-107110·WC-121·WC-122). KGcode노드 populator·code-graphMCP 시드·ephemeral overlay·ingest/overlay 라우트(S1~S3) + 실graphifyy0.8.28 대조 매퍼(S4 매퍼, WC-121) + 프로듀서 CLIworkcell code-graph from-repo+ingest-graphify라우트(S4 운영, WC-122). 남은 = graphify를 실 런타임에 설치하는 배포 선택뿐(코드 아님). S5(KG를 사람용 UI로 노출)=선택, 가치 불확실(KG는 에이전트용 support 서브시스템 — MCP/overlay/컨텍스트주입으로 이미 소비; 사람 브라우징 뷰어는 별도 product call). (※ plan-report 언어 설정 UI + portability = 이미 완료 WC-89/90.) - i18n 전체앱 한국어 사실상 완료(WC-82~95) — ~46페이지 + ~32컴포넌트 + 전 크롬(
3400키). 잔여=진짜 long-tail: dev 전용 페이지(UxLab/DesignGuide/DashboardLive — 의도적 제외, 제품가치 0)·12문자열 미세 컴포넌트(무시가능). 38개 비-한국어 언어는 부분 로케일+영어 폴백 구조만 깔림 — 채우려면 네이티브 리뷰가 필요한 별도 번역 파이프라인(검증 불가 언어 기계번역 안 함). - pair 협업 (사용자 "상용화 서비스 급 퀄리티" 지시) — ✅ 완료: 모델선택(WC-97)·기존-워크스페이스 grounding(WC-103)·env-secret(WC-125)·타임라인 투명성(WC-126)·수렴까지 실행(WC-127)·동시성 race가드(WC-128)·파일편집 워크스페이스 D21 완료(WC-130~133: realize seam→ensurePairWorkspace→executor 배선→cleanup, 실git+embedded-pg 테스트·heartbeat 무회귀). 잔여=선택적 정제(예: 워크스페이스 자동 idle-reaper는 시스템 전역 부재=parity·D21 무관). live 운영=
WORKCELL_PAIR_LIVE_LLM=1+WORKCELL_PAIR_LIVE_WORKSPACE=1. - substantive(외부/결정 필요): Workcell-MCP 자동주입 · remote MCP relay · 실 Open Design 데몬 · image pixel diff.
- 수용·문서화: create-as-done은 proof-gate 미적용(닭-달걀, 의도).
- 로컬 체크 =
pnpm typecheck. - UI vitest =
pnpm --filter @workcell/ui exec vitest run [pattern]. - 서버 vitest =
pnpm --filter @workcell/server exec vitest run <file>(개별/소배치). - 머지게이트 = full e2e(
pnpm test:e2e) 필수 — signoff 회귀 포착 지점.
- 기본 명시 승인 필요. 병렬 lane/worktree는
issueflow parallel있을 때만. blocked는PAUSED유지.
- 원본 기획서
PRODUCT_SPEC.md(+「구현 그라운딩」=코드 이식 지도) · 앵커PLAN_ANCHOR.md - 계획
docs/plan/PLAN.md(§9 = 구현 상태 어노테이션 포함) · 로드맵docs/plan/ROADMAP.md(Phase 1 진행 박스 포함) · 결정docs/plan/DECISIONS.md(D1~D22; D22=Open Design 시안=source of truth) · stripdocs/plan/STRIP_PLAN.md - 슬라이스별 압축 메모:
memory/workcell-project.md(해시·근거·테스트 카운트 보존)