diff --git a/README.md b/README.md index 9093b5b6e..13d1935f6 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,14 @@ naia-shell은 **Naia Visual Agent를 위한 데스크톱 셸(앱)**이다. 사 | **텍스트 대화** | 셸이 agent 를 spawn → gRPC `Chat` 스트림으로 대화. new core 경로 | ✅ | | **provider/모델 설정** | 설정 UI → agent `SetWorkspace`/reload 로 앱 재시작 없이 모델 교체 | ✅ | | **환경 사이드카** | 뇌와 분리된 독립 서비스(예: BGM `bgm-sidecar`). 뇌가 죽어도 생존 | ✅ | +| **선제 발화 profile** | “개인 라디오 시작해” 또는 “행사 소개 시작”으로 opt-in. 장기 activity stream을 기존 텍스트·TTS·BGM 표현에 연결 | ✅ 기술 slice / 제품 검증은 부분 | | **음성 I/O · 아바타 · TTS** | 음성 입출력·아바타 표현 | 🔜 옛 경로 존재, 새 코어 이식은 UC2 후속 | | **다중 모달 표현** | 하나의 표현 의도를 음성/아바타/로봇 몸체가 함께 소비 | 🔜 egress 분화 자리 예약 | > ✅ = 새 코어에 구현+테스트(Playwright e2e 포함). 🔜 = 설계/옛 경로 존재, 이식 진행. > 정확한 추적은 `docs/user-scenarios.md`(UC SoT)와 `docs/requirements.md` 참조. +> 실제 Tauri 검증은 profile 저장·복원, DJ 실제 YouTube BGM·첫 결과·stop, 전시 greeting·stop까지다. +> audible TTS·live 질문 끼어들기·모든 제어·장시간/현장 품질은 아직 native 검증으로 주장하지 않는다. --- diff --git a/docs/acceptance-criteria.md b/docs/acceptance-criteria.md index 441e16408..1dee2eeef 100644 --- a/docs/acceptance-criteria.md +++ b/docs/acceptance-criteria.md @@ -6,6 +6,20 @@ 이 문서는 자율 작업에서 **"완료선언 ≠ 완료"** 를 구조적으로 막는 방법론이다. 2026-05-30 alpha-n-naia-144 실험(placeholder glyph 가 "17/17 통과"로 넘어간 드리프트)에서 도출. +## UC15 선제 발화 증거 경계 + +| 흐름 | 계약/통합 | 실제 Tauri | 제품/현장 | +|---|:---:|:---:|:---:| +| profile 저장·복원 | ✅ | ✅ | 미검증 | +| DJ 실제 YouTube BGM·첫 결과·stop | ✅ | ✅ | 미검증 | +| 전시 greeting·stop | ✅ | ✅ | 미검증 | +| DJ 멘트2·모든 제어·live 끼어들기 | ✅ | 미검증 | 미검증 | +| 전시 질문 중단→답변→resume·stale audio 폐기 | ✅ | 미검증 | 미검증 | +| audible proactive TTS·장시간 품질 | seam 계약 | 미검증 | 미검증 | + +`71-proactive-speech-profiles.spec.ts`는 TTS를 끄고 실행한다. 따라서 이 표의 실제 Tauri 열보다 넓은 +완료 주장을 하지 않는다. 남은 제품 acceptance는 `nextain/naia-agent#84`에서 추적한다. + ## 1. 동기 (실측된 드리프트) 한 프로젝트가 한글 폰트를 "구현 완료, 17/17 테스트 통과"로 인도했다. 그러나: diff --git a/docs/progress/01.requirements/INDEX.md b/docs/progress/01.requirements/INDEX.md index da2b8fb60..6e941aabe 100644 --- a/docs/progress/01.requirements/INDEX.md +++ b/docs/progress/01.requirements/INDEX.md @@ -26,6 +26,7 @@ scripts/check-traceability.mjs 가 이 표를 파싱한다. (ID 형식 = REQ-### | REQ-009 | 도구/스킬 | 에이전트 **도구루프 + skills**(time/weather/memo/github/obsidian/mcp 등, agent-local) | In-progress | UC-005 | SPEC-006 | TEST-S-005 | | REQ-010 | 브라우저 | 에이전트 **브라우저 조작**(navigate/click/fill/snapshot, agent-local + 외부 CLI) | Draft | UC-006 | SPEC-006 | TEST-S-006 | | REQ-011 | 유투브/BGM | **공간 분위기 BGM**(youtube search/play/volume, agent-local + shell player) | Draft | UC-008 | SPEC-006 | TEST-S-008 | +| REQ-012 | 선제 발화 profile | 개인 라디오 DJ·전시 소개 activity를 gRPC로 구독해 기존 텍스트/TTS/BGM에 표현하고 yield/control/stop 전달. native 증적은 시작/BGM 또는 greeting/stop까지 | Partial | UC-015 | SPEC-009 | TEST-S-015 | ## 비기능 요구사항 (NFR → REQ, 횡단 — TEST-S 직결) diff --git a/docs/progress/02.user-scenarios/INDEX.md b/docs/progress/02.user-scenarios/INDEX.md index 27d8ef244..907dd7a9b 100644 --- a/docs/progress/02.user-scenarios/INDEX.md +++ b/docs/progress/02.user-scenarios/INDEX.md @@ -23,6 +23,7 @@ | UC-006 | ⑦브라우저 | 에이전트가 브라우저를 조작(navigate/click/fill) (구 UC6) | REQ-010 | Draft | TEST-S-006 | | UC-011 | 자기상태 | naia 가 자기 상태(시스템/진단/장치)를 정직 보고 (구 UC11/UC14) | REQ-002 | In-progress | TEST-S-011 | | UC-007 | ⑨워크스페이스(제외) | 에이전트가 host-system 관측·조작(승인) (구 UC7/UC7a) — **현 스코프 제외**(DriftDetector/MutationGate=old 소비자 부재, 방향 미해결) | REQ-004, REQ-005 | Draft | TEST-S-007 | +| UC-015 | 선제·연속 발화 | 개인 라디오 DJ와 회사 전시 소개가 무입력으로 시작하고 기존 표현 경로를 사용. 계약 흐름 구현, native는 시작/BGM 또는 greeting/stop 경계 | REQ-012 | Partial | TEST-S-015 | > **제외(루크 2026-06-15)**: ⑧메모리(구 UC3/UC4)=off-scope(naia-memory 다른 세션 소유, canon out_of_scope). ⑨워크스페이스(UC-007)=보류(신규발명 방향 결정 후). > 상세 granular(S01~71 — provider/voice/skills/channels 60+)·테스트 커버리지 맵 = `docs/user-scenarios.md`. diff --git a/docs/progress/03.uc-tests/INDEX.md b/docs/progress/03.uc-tests/INDEX.md index 97f43e239..72532632a 100644 --- a/docs/progress/03.uc-tests/INDEX.md +++ b/docs/progress/03.uc-tests/INDEX.md @@ -23,6 +23,7 @@ | TEST-S-007 | UC-007 | workspace 관측/조작 | e2e | `packages/shell/e2e/91-workspace-panel.spec.ts`(old-path) | Planned | | TEST-S-008 | UC-008 | youtube BGM 검색/재생 | e2e | — | Planned | | TEST-S-011 | UC-011 | 자기상태/진단 보고(Diagnostics RPC) | e2e | — | Planned | +| TEST-S-015 | UC-015 | 계약: activity 표시/TTS·yield/control/stop·stale 폐기. 실제 Tauri: profile 저장·복원, DJ 실제 BGM·첫 결과·stop, 전시 greeting·stop | 계약+부분 native | `packages/shell/src/lib/__tests__/speech-profile-commands.test.ts`, `packages/shell/e2e-tauri/specs/71-proactive-speech-profiles.spec.ts` | Partial | | TEST-S-101 | REQ-101 | substrate-agnostic: core 가 @tauri-apps 미의존(import 검사) | 정적 | (compile-integrity + import 검사) | Planned | | TEST-S-103 | REQ-103 | provenance: provider 출처 추적(actor/correlation) | e2e | `packages/shell/e2e/uc-provider-provenance-live.spec.ts` | Pass | | TEST-S-104 | REQ-104 | 계약 드리프트 0토큰 결정론 게이트 | 게이트 | `scripts/conform/` (conform-gate) | Planned | diff --git a/docs/progress/04.features/INDEX.md b/docs/progress/04.features/INDEX.md index 61129d5e5..1284fffad 100644 --- a/docs/progress/04.features/INDEX.md +++ b/docs/progress/04.features/INDEX.md @@ -23,6 +23,7 @@ area = core(헥사고날 코어 @nextain/naia-os-core) / shell(packages/shell) / | SPEC-006 | UC-001 | **UC1 ChatService gRPC** — startTurn/cancel/deliverChunk + chat-bridge + shell-compat(makeShellChatService) + 도구/승인 페이로드 포워딩. ports/uc1·app/chat·adapters | core | Done(graft+e2e) | TEST-F-006 | | SPEC-007 | UC-012 | **UC12 OnboardingController** — submit/assets/startNaiaAuth/onNaiaAuthCallback/complete/completeWith(§D 신규계약)/update. app/control/onboarding | core | In-progress(completeWith graft 완료, 나이아계정 OAuth 흐름 graft 미연결) | TEST-F-007 | | SPEC-008 | UC-002 | **V2 음성** — SensoryPort/ExpressionPort/VoiceProviderPort + os-local 어댑터(AudioPlayer/MicCapture/STT/avatar). ports/v2·domain/voice | core | Done(os-local 이식, external WS=루크머신) | TEST-F-008 | +| SPEC-009 | UC-015 | **선제 발화 전달** — persisted profile 설정, session activity stream, 기존 agent_response/TTS/BGM 표현, yield/control/stop, generation 기반 stale 폐기 | shell+Rust | Partial(native acceptance 후속) | TEST-F-009 | ## 비고 - **graft 미연결(dormant)** = SPEC 은 이식+리뷰+컴파일 완료지만 실행 셸(packages/shell)이 아직 new-core 경유 안 함(old 경로 구동). graft 완료 = SPEC-006(chat)·SPEC-003(승인, chat흐름 내)·SPEC-007 일부(completeWith). diff --git a/docs/progress/05.features-tests/INDEX.md b/docs/progress/05.features-tests/INDEX.md index 431f84a68..91cb36f35 100644 --- a/docs/progress/05.features-tests/INDEX.md +++ b/docs/progress/05.features-tests/INDEX.md @@ -22,6 +22,7 @@ | TEST-F-006 | SPEC-006 | UC1 chat/bridge/shell-compat/chunk-fields/child-stdio/trace 계약·통합 | `src/test/uc1-chat.contract.test.ts`, `uc1-bridge.contract.test.ts`, `uc1-shell-compat.contract.test.ts`, `uc1-chunk-fields.test.ts`, `uc1-child-stdio.contract.test.ts`, `uc1-trace.integration.test.ts` | Pass | | TEST-F-007 | SPEC-007 | UC12 OnboardingController 계약(stale키 strip·completeWith·complete 가드) | `src/test/uc12-onboarding-controller.contract.test.ts`, `uc12-onboarding.contract.test.ts` | Pass | | TEST-F-008 | SPEC-008 | V2 voice 도메인(startup-lazy) + live-adapter(parity) | `src/test/v2-voice-domain.test.ts`, `v2-live-adapter.test.ts` | Pass | +| TEST-F-009 | SPEC-009 | parser·session/profile generation·yield/control/stop은 계약 테스트. 실제 Tauri는 DJ BGM/첫 text와 전시 greeting, stop만; TTS·live barge-in은 미검증 | `packages/shell/src/lib/__tests__/speech-profile-commands.test.ts`, `packages/shell/e2e-tauri/specs/71-proactive-speech-profiles.spec.ts` | Partial | ## 비고 - Pass = `npx vitest run src/test` 191 cases pass(2026-06-15). `.mjs` 가드 파일 17개는 vitest `process.exit` 아티팩트로 파일레벨 RED 처럼 보이나 케이스 전부 통과(`feedback_test_exit_code_not_pass_count`). diff --git a/docs/progress/99.dev-comm/assembly-matrix-2026-06-10.md b/docs/progress/99.dev-comm/assembly-matrix-2026-06-10.md index 621e67e62..37fab7fed 100644 --- a/docs/progress/99.dev-comm/assembly-matrix-2026-06-10.md +++ b/docs/progress/99.dev-comm/assembly-matrix-2026-06-10.md @@ -43,7 +43,7 @@ --- -## 수직 UC 트랙 — UC1~14 전수 (분류; UC1 상세) +## 수직 UC 트랙 — UC1~15 전수 (분류; UC1 상세) | UC | 이식/보충 | 주 인지포트 | 권위 | slice/상태 | |---|---|---|---|---| @@ -66,6 +66,7 @@ | UC13 승인게이트 | 이식+보충 | ApprovalPort | mixed | **F1 계약+코드** | | UC13a 중단/e-stop | **보충** | SafetyPort | scenario | pending(H-safety) | | UC14 degradation | **보충** | InteroceptivePort→Express | scenario | **F1(정직 degradation 신설)** | +| UC15 자유·연속 발화 전달 | 이식+보충 | Agent gRPC→ExpressionPort·SafetyPort | naia-agent #82 contract | **기술 slice 구현, native acceptance partial** | ### UC1 상세 — 텍스트 대화 (Chat→사고→표현) | # | 조각(S) | old | 이식/보충 | 인지 포트 | fit | 권위 | 상태 | diff --git a/docs/requirements.md b/docs/requirements.md index e1d3c752f..bdb285530 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -262,8 +262,8 @@ localStorage `naia-config` 는 파일에서 하이드레이트되는 **순수 |---|---|---|:---:| | **FR-CONT-SHELL.1** | agent 연결 뒤 persisted proactive profile을 `ConfigureSpeechProfile`로 전달하고, 비어 있지 않은 현재 session별 `SubscribeSpeechActivities` 장기 stream을 최대 하나 구독해 self-init `AgentEvent.request_id`, `activity_id`를 보존한다. | Rust contract + live gRPC | Implemented | | **FR-CONT-SHELL.2** | activity `AgentEvent`를 기존 `agent_event_to_ui_json`→`agent_response` 경로로 보내 텍스트·usage·finish/error의 기존 표현 소비자를 재사용한다. 별도 event union·반복 상태 기계는 만들지 않는다. | Rust transcode + 기존 chat event 회귀 | Implemented | -| **FR-CONT-SHELL.3** | 자유 발화 cancel은 관측한 requestId+activityId를 agent `Cancel`에 돌려주고, activityId 관측 전 또는 session 명시 정지는 thin `StopSpeechActivity` command/RPC로 위임한다. requestGeneration은 requested Chat에만 사용하며 ordinary Chat cancel과 self-init activity cancel을 혼합하지 않는다. | Rust unary contract + e2e-tauri | Implemented | -| **FR-CONT-SHELL.7** | unsolicited activity는 ordinary Chat의 currentRequestId 필터와 별도 activity ref로 수용한다. 새 사용자 입력은 queue보다 먼저 `interruptTts → YieldSpeechActivity → activityResume 토큰을 실은 Chat` 순서로 처리하고, 이전 activityId/profileGeneration의 늦은 text/TTS를 폐기한다. quiet/stop은 Yield 대신 Stop을 사용한다. | ChatArea contract + e2e-tauri ordering | Implemented | +| **FR-CONT-SHELL.3** | 자유 발화 cancel은 관측한 requestId+activityId를 agent `Cancel`에 돌려주고, activityId 관측 전 또는 session 명시 정지는 thin `StopSpeechActivity` command/RPC로 위임한다. requestGeneration은 requested Chat에만 사용하며 ordinary Chat cancel과 self-init activity cancel을 혼합하지 않는다. | Rust unary contract; native Tauri는 stop만 | Implemented | +| **FR-CONT-SHELL.7** | unsolicited activity는 ordinary Chat의 currentRequestId 필터와 별도 activity ref로 수용한다. 새 사용자 입력은 queue보다 먼저 `interruptTts → YieldSpeechActivity → activityResume 토큰을 실은 Chat` 순서로 처리하고, 이전 activityId/profileGeneration의 늦은 text/TTS를 폐기한다. quiet/stop은 Yield 대신 Stop을 사용한다. | ChatArea contract; native ordering은 미검증 | Implemented | | **FR-CONT-SHELL.4** | subscriber disconnect/dispatcher 종료/agent 재시작에서 stream task와 client 자원을 정리한다. server가 해당 session active를 cancelled로 끝낼 수 있도록 stream을 실제 drop한다. | disconnect live test + watchdog | Implemented | | **FR-CONT-SHELL.5** | 같은 session 중복 구독 0, 서로 다른 session은 독립 구독, 요청 기반 기존 Chat stream과 일반 cancel/event correlation 무회귀를 보장한다. | duplicate/session isolation + existing Chat tests | Implemented | | **FR-CONT-SHELL.6** | transcript read/delete의 session basename은 agent writer와 동일하다. canonical `[A-Za-z0-9_-]+` 최대 128자는 그대로, 나머지는 UTF-8 SHA-256 전체 hex를 쓰고 양쪽 공통 벡터를 검증한다. | Rust `safe_session_base` + agent shared-vector contract | Pending (후속 hardening) | @@ -272,6 +272,11 @@ NFR: agent-owned(반복·deadline·memory를 셸에 복제 금지), bounded(sess 현재 session 집합에 한정), wire-compatible(기존 AgentEvent union 재사용), observable(debug 로그에 session subscribe/start/terminal/disconnect와 requestId, 시크릿 없음). +검증 경계: PR #381의 실제 Tauri 테스트는 profile 저장·복원, DJ 실제 YouTube BGM·첫 결과·stop, +전시 greeting·stop을 검증한다. 이 테스트는 `ttsEnabled: false`이므로 audible proactive TTS, +DJ 멘트2, 질문 barge-in→답변→resume, 모든 제어, stale audio 폐기를 native로 증명하지 않는다. +그 흐름은 현재 Rust/프론트/agent 계약·통합 테스트 증거이며, 제품 live acceptance는 agent #84 후속이다. + ## 비기능 요구사항 (NFR) — 횡단(전 tranche) | ID | 요구사항 | 근거(1단계 구조) | diff --git a/docs/user-scenarios.md b/docs/user-scenarios.md index 28425c0eb..f18480a9d 100644 --- a/docs/user-scenarios.md +++ b/docs/user-scenarios.md @@ -351,10 +351,13 @@ naia-agent가 사용자 요청의 기존 `Chat` stream에서 연속 발화를 P02 검증: - Rust 단위/계약: `agent_grpc.rs`의 activity event 변환, subscribe/stop 요청, 같은 session 중복 구독 방지. -- 실 백엔드: `agent_grpc.rs` live test 또는 e2e-tauri에서 agent spawn → session subscribe → self-init +- 실 백엔드/계약: `agent_grpc.rs`가 agent spawn → session subscribe → self-init text/usage/finish → 기존 `agent_response`, requestId cancel/stop, disconnect 정리를 검증. - 프론트 계약: ChatArea에서 unsolicited activity 표시/TTS, `interruptTts → yield/stop → Chat` 순서, - stale audio/text 폐기를 검증한다. e2e-tauri에서 개인 DJ BGM play/stop과 전시 질문 barge-in을 검증한다. + stale audio/text 폐기를 검증한다. +- 실제 Tauri `71-proactive-speech-profiles.spec.ts`: profile 저장·복원, 개인 DJ의 실제 YouTube BGM·첫 + 결과 text·stop, 전시 greeting·stop만 검증한다. 이 테스트는 TTS를 꺼 두므로 audible TTS, DJ 멘트2, + 전시 질문 barge-in→답변→resume, 모든 control, stale audio 폐기를 native로 증명하지 않는다. Test Coverage Map: UC15 / FR-CONT-SHELL.1~7 → Rust `agent_grpc` contract+live tests, -`packages/shell/e2e-tauri` activity stream full-stack, 기존 `src/main/adapters/tauri/uc1`·ChatArea cancel 회귀. +`packages/shell/e2e-tauri` 시작/표현 일부 full-stack, 기존 `src/main/adapters/tauri/uc1`·ChatArea cancel 회귀.