Skip to content

feat: Epic 13 (Structured Questions) + Epic 14 (Native STT)#8

Draft
don-petry wants to merge 52 commits into
mainfrom
worktree-implement-sprint-1
Draft

feat: Epic 13 (Structured Questions) + Epic 14 (Native STT)#8
don-petry wants to merge 52 commits into
mainfrom
worktree-implement-sprint-1

Conversation

@don-petry
Copy link
Copy Markdown
Contributor

@don-petry don-petry commented Mar 24, 2026

Summary

  • Epic 13 (FR57-FR59): Structured Question Input — When the agent responds with numbered questions, they're detected by a renderer-side parser and presented as interactive Question Card Stack in the right panel with per-question input, suggestion chips, dot navigation, and review-before-submit
  • Epic 14 (FR60-FR62): Native STT with sherpa-onnx — Replaced non-functional Web Speech API (Google servers unreachable in Electron) with sherpa-onnx local streaming speech recognition. Audio captured via getUserMedia, streamed as 16kHz PCM to main process via IPC, transcribed in real-time with endpoint detection
  • Bug fix: Removed hard-coded option cards — Option cards are now agent-driven only, not hard-coded on load

Commits (stacked)

  1. docs: Spec updates — PRD v2.4 (FR57-FR62), UX spec v1.3 (Journey 7), Epics (Epic 13 stories), Sprint status
  2. feat: Epic 13 implementation — question-parser.ts, QuestionCardStack, QuestionReview, SuggestionChips, E2E tests
  3. feat: Epic 14 implementation — sherpa-onnx-node adapter, IPC audio pipeline, mic permission handlers

New Files

File Purpose
src/shared/types/domain/question-parser.ts Pure parser: detects numbered questions in agent text
src/renderer/components/session/QuestionCardStack.tsx Card stack with useReducer, dot nav, chips
src/renderer/components/session/QuestionReview.tsx Review overlay with aggregated submission
src/renderer/components/session/SuggestionChips.tsx Selectable pill chips
src/main/speech/sherpa-onnx-stt.ts Main-process STT adapter using sherpa-onnx OnlineRecognizer
src/main/ipc/speech-ipc-handler.ts IPC bridge for audio streaming
src/renderer/speech/ipc-speech-stt.ts Renderer-side STT via getUserMedia + IPC

Test plan

  • 78 unit/component tests passing (question parser, card stack, review, ConversationView)
  • TypeScript clean (tsc --noEmit)
  • ESLint clean (--max-warnings 0)
  • Pre-commit hooks passing (lint-staged + tsc + vitest related)
  • E2E: structured-questions.spec.ts (requires built app + Claude subscription)
  • Manual: mic button → sherpa-onnx transcription → agent message
  • Manual: agent multi-question response → card stack appears → answer → submit

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 24, 2026 23:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR scaffolds the TalkTerm Electron (Forge + Vite) application and establishes the initial shared domain/port types, renderer UI components (setup + overlay/display primitives), speech adapters, and CI/tooling configuration.

Changes:

  • Add project build/test/tooling configuration (Vite configs, TS strict config, ESLint/Prettier/Husky, Vitest + coverage, Tailwind/PostCSS, GitHub Actions).
  • Introduce shared domain + port types (sessions, writeback, IPC envelopes/channels, agent events/errors, value objects).
  • Implement initial renderer components/hooks and main-process stubs/utilities with accompanying unit tests.

Reviewed changes

Copilot reviewed 147 out of 162 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
vitest.config.ts Adds Vitest configuration with coverage thresholds.
vite.renderer.config.ts Configures PostCSS pipeline for the renderer build.
vite.preload.config.ts Adds preload Vite config placeholder.
vite.main.config.ts Adds main-process Vite resolution settings.
tsconfig.json Enables strict TypeScript options and path mapping.
test/setup-renderer.ts Adds Testing Library jest-dom matchers for Vitest.
tailwind.config.ts Defines design tokens (colors/type/spacing) for Tailwind.
src/shared/types/ports/text-to-speech.ts Defines TextToSpeech port contract.
src/shared/types/ports/speech-to-text.ts Defines SpeechToText port contract.
src/shared/types/ports/agent-backend.ts Defines AgentBackend port + session config.
src/shared/types/ports/.gitkeep Keeps ports directory in VCS.
src/shared/types/domain/writeback.ts Adds writeback target types + default selection by origin.
src/shared/types/domain/writeback.test.ts Tests default writeback selection.
src/shared/types/domain/value-objects.ts Adds branded value objects + constructors.
src/shared/types/domain/value-objects.test.ts Tests value-object validation.
src/shared/types/domain/user-profile.ts Adds UserProfile type + factory.
src/shared/types/domain/user-profile.test.ts Tests UserProfile factory behavior.
src/shared/types/domain/session.ts Adds Session type + factory.
src/shared/types/domain/session.test.ts Tests Session factory behavior.
src/shared/types/domain/project.test.ts Sanity tests for TS strictness flags.
src/shared/types/domain/preference.ts Adds preference threshold utilities/constants.
src/shared/types/domain/preference.test.ts Tests preference threshold logic.
src/shared/types/domain/launch-state.ts Adds setup-step routing helper.
src/shared/types/domain/launch-state.test.ts Tests setup-step routing priority.
src/shared/types/domain/ipc-message.ts Adds IPC message envelope + factory.
src/shared/types/domain/ipc-message.test.ts Tests IPC message factory timestamping.
src/shared/types/domain/ipc-channels.ts Defines canonical IPC channel constants + type.
src/shared/types/domain/ipc-channels.test.ts Tests channel naming + uniqueness.
src/shared/types/domain/electron-api.ts Declares preload bridge contract type for renderer.
src/shared/types/domain/avatar-persona.ts Adds MVP persona roster.
src/shared/types/domain/avatar-persona.test.ts Tests persona roster shape/uniqueness.
src/shared/types/domain/audit-entry.ts Adds AuditEntry type + factory.
src/shared/types/domain/audit-entry.test.ts Tests AuditEntry factory behavior.
src/shared/types/domain/api-key-state.ts Defines API key state + validation result types.
src/shared/types/domain/agent-event.ts Adds AgentEvent union for streaming events.
src/shared/types/domain/agent-error.ts Adds error classification + user-friendly messaging.
src/shared/types/domain/agent-error.test.ts Tests error classification and message safety.
src/shared/types/domain/.gitkeep Keeps domain directory in VCS.
src/renderer/types/web-speech.d.ts Adds Web Speech API window typings.
src/renderer/types/.gitkeep Keeps renderer types directory in VCS.
src/renderer/styles/global.css Adds Tailwind directives + global font/body styles.
src/renderer/speech/web-speech-tts.ts Implements TTS adapter using Web Speech API.
src/renderer/speech/web-speech-tts.test.ts Tests WebSpeechTts behavior under jsdom mocks.
src/renderer/speech/web-speech-stt.ts Implements STT adapter using Web Speech API.
src/renderer/speech/web-speech-stt.test.ts Tests WebSpeechStt behavior under jsdom mocks.
src/renderer/speech/barge-in-controller.ts Adds barge-in state controller (TTS stop + STT start).
src/renderer/speech/barge-in-controller.test.ts Tests barge-in state transitions and calls.
src/renderer/speech/.gitkeep Keeps speech directory in VCS.
src/renderer/renderer.tsx Adds React entrypoint mounting App.
src/renderer/hooks/useLayoutState.ts Adds reducer-driven layout mode hook.
src/renderer/hooks/useLayoutState.test.ts Tests layout mode transitions.
src/renderer/hooks/useDisplayMode.ts Adds display-mode selection state hook.
src/renderer/hooks/useDisplayMode.test.ts Tests display-mode state updates.
src/renderer/hooks/useAvatarState.ts Adds avatar animation-state reducer hook.
src/renderer/hooks/useAvatarState.test.ts Tests avatar state transitions.
src/renderer/hooks/.gitkeep Keeps hooks directory in VCS.
src/renderer/context/.gitkeep Keeps context directory in VCS.
src/renderer/components/setup/WorkspaceSelection.tsx Adds workspace selection UI.
src/renderer/components/setup/WorkspaceSelection.test.tsx Tests workspace selection callbacks.
src/renderer/components/setup/ProfileSetup.tsx Adds profile setup UI.
src/renderer/components/setup/ProfileSetup.test.tsx Tests profile setup interactions.
src/renderer/components/setup/AvatarSelection.tsx Adds avatar selection UI using MVP personas.
src/renderer/components/setup/AvatarSelection.test.tsx Tests avatar selection callback.
src/renderer/components/setup/ApiKeySetup.tsx Adds API key entry/validation UI (stub validation).
src/renderer/components/setup/ApiKeySetup.test.tsx Tests API key setup basic rendering.
src/renderer/components/setup/AdminBlockScreen.tsx Adds blocking admin-privileges screen UI.
src/renderer/components/setup/AdminBlockScreen.test.tsx Tests admin block screen rendering and buttons.
src/renderer/components/overlay/ActionPanel.tsx Adds action panel container (listbox) UI.
src/renderer/components/overlay/ActionPanel.test.tsx Tests action panel rendering and selection.
src/renderer/components/overlay/ActionCard.tsx Adds action card UI with option semantics.
src/renderer/components/overlay/ActionCard.test.tsx Tests action card semantics and interactions.
src/renderer/components/display/TaskProgress.tsx Adds task progress display mode.
src/renderer/components/display/TaskProgress.test.tsx Tests task progress rendering.
src/renderer/components/display/DocumentView.tsx Adds basic document display mode.
src/renderer/components/display/DocumentView.test.tsx Tests document rendering/path display.
src/renderer/components/display/ComparisonTable.tsx Adds comparison table display mode.
src/renderer/components/display/ComparisonTable.test.tsx Tests comparison table expand behavior.
src/renderer/components/display/ClusteredCards.tsx Adds clustered cards display mode.
src/renderer/components/display/ClusteredCards.test.tsx Tests clustered cards expand/priority tags.
src/renderer/components/display/ActivityFeed.tsx Adds activity feed display mode.
src/renderer/components/display/ActivityFeed.test.tsx Tests activity feed visibility/rendering.
src/renderer/components/avatar/TextInput.tsx Adds text input w/ Enter-to-send and mic button.
src/renderer/components/avatar/TextInput.test.tsx Tests text input send/newline/mic behavior.
src/renderer/components/avatar/StatusIndicator.tsx Adds avatar status pill.
src/renderer/components/avatar/StatusIndicator.test.tsx Tests status pill rendering and role=status.
src/renderer/components/avatar/CaptionBar.tsx Adds caption bar w/ delayed hide logic.
src/renderer/components/avatar/CaptionBar.test.tsx Tests caption bar basic rendering conditions.
src/renderer/components/.gitkeep Keeps components directory in VCS.
src/renderer/App.tsx Adds minimal App shell.
src/preload/preload.ts Adds preload bridge stub via contextBridge.
src/main/storage/.gitkeep Keeps storage directory in VCS.
src/main/security/admin-check.ts Adds admin privilege check helper + instructions.
src/main/security/admin-check.test.ts Tests admin privilege check across platforms.
src/main/security/.gitkeep Keeps security directory in VCS.
src/main/main.ts Adds Electron main window creation and lifecycle wiring.
src/main/ipc/.gitkeep Keeps IPC directory in VCS.
src/main/agent/fake-agent-backend.ts Adds FakeAgentBackend test double.
src/main/agent/fake-agent-backend.test.ts Tests FakeAgentBackend event streaming/cancel.
src/main/agent/agent-message-router.ts Adds router forwarding backend events to handler.
src/main/agent/agent-message-router.test.ts Tests router event forwarding and cancel.
src/main/agent/.gitkeep Keeps agent directory in VCS.
postcss.config.js Adds PostCSS config for Tailwind/autoprefixer.
package.json Adds dependencies, scripts, lint-staged config.
index.html Adds renderer entry HTML.
forge.config.ts Configures Electron Forge + Vite plugin + makers.
eslint.config.mjs Adds ESLint flat config with strict TS + React rules.
.prettierrc Adds Prettier config.
.prettierignore Adds Prettier ignore rules.
.husky/pre-commit Adds pre-commit quality gate commands.
.gitignore Adds repo ignore rules (build artifacts/coverage).
.github/workflows/release.yml Adds release workflow for tag builds.
.github/workflows/ci.yml Adds CI workflow with 3-OS matrix quality gates.
_bmad-output/implementation-artifacts/*.md Adds planning/implementation artifacts for stories.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +7
beforeEach(() => {
vi.restoreAllMocks();
});
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vi.stubGlobal('process', ...) is not undone by vi.restoreAllMocks(), so the stubbed process can leak into other tests (and even across suites). Add an afterEach/afterAll that calls vi.unstubAllGlobals() (or vi.unstubGlobal('process')) to restore the real process object.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +10
export interface IPCMessage<T> {
channel: string;
payload: T;
timestamp: number;
}

export function createIPCMessage<T>(channel: string, payload: T): IPCMessage<T> {
return {
channel,
payload,
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPCMessage.channel and createIPCMessage(channel, ...) are typed as plain string, which defeats the point of having IPCChannel in ipc-channels.ts. Consider typing channel as IPCChannel (and optionally making the fields readonly) so invalid channel names can’t compile.

Copilot uses AI. Check for mistakes.
Comment thread .github/workflows/ci.yml
Comment on lines +5 to +7
branches: [main]
pull_request:
branches: [main]
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is configured to run only for pushes to main. That means branch pushes won’t get CI feedback until a PR is opened (or merged). If the intent is to enforce quality gates on every push (as described in the planning artifacts), expand the push trigger to all branches (or at least your protected branches / release branches).

Suggested change
branches: [main]
pull_request:
branches: [main]
branches:
- '**'
pull_request:
branches:
- '**'

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +24
- run: npm ci --legacy-peer-deps
- run: npm run make
- uses: actions/upload-artifact@v4
with:
name: talkterm-${{ matrix.os }}
path: out/make/**/*
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Release workflow currently only uploads build artifacts to the workflow run via actions/upload-artifact, but it doesn’t create/update a GitHub Release or attach the artifacts to the release. If the goal is a release pipeline, add steps to publish to GitHub Releases (e.g., via Electron Forge publisher or softprops/action-gh-release) so tagged builds are actually downloadable from the release page.

Copilot uses AI. Check for mistakes.
Comment thread src/shared/types/domain/agent-error.ts Outdated
Comment on lines +32 to +35
if (msg.includes('permission') || msg.includes('eacces')) return 'file-permission';
if (msg.includes('speech') || msg.includes('recognition')) return 'stt-error';
if (msg.includes('synthesis') || msg.includes('tts')) return 'tts-error';
if (msg.includes('sdk') || msg.includes('agent')) return 'sdk-error';
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

classifyError checks for 'speech' before checking for TTS keywords. Errors like "speech synthesis failed" will currently be classified as stt-error instead of tts-error because they contain both substrings. Reorder these checks (TTS before STT) or tighten the STT match (e.g., require recognition/speech recognition).

Copilot uses AI. Check for mistakes.
Comment thread src/shared/types/domain/agent-event.ts Outdated
Comment on lines +6 to +14
| { type: 'error'; userMessage: string; recoveryOptions: RecoveryOption[] }
| { type: 'complete'; summary: string }
| { type: 'progress'; step: string; status: 'pending' | 'in-progress' | 'completed' | 'failed' };

export interface RecoveryOption {
label: string;
action: string;
description: string;
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RecoveryOption is defined here and also separately in agent-error.ts, which makes it easy for the shapes to drift and forces extra mapping later. Prefer exporting a single RecoveryOption type (e.g., from agent-error.ts) and reusing it in AgentEvent to keep error/recovery payloads consistent across the app.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +26
<div className="flex h-full w-[240px] flex-col gap-3 overflow-y-auto bg-surface-muted p-4">
<h2 className="text-subtitle text-text-on-dark">{title}</h2>
<div className="flex flex-col gap-3" role="listbox">
{cards.map((card) => (
<ActionCard
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The panel is marked role="listbox", but there’s no keyboard interaction implemented for moving focus/selection with arrow keys (and the listbox isn’t associated with an accessible name via aria-label/aria-labelledby). To meet the stated keyboard-nav behavior, add an onKeyDown handler with roving tabindex (or aria-activedescendant) so Up/Down changes the active option and Enter/Space selects.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +20
describe('CaptionBar', () => {
it('renders text when visible', () => {
render(<CaptionBar text="Hello world" visible={true} />);
expect(screen.getByText('Hello world')).toBeInTheDocument();
});

it('renders nothing when text is null', () => {
const { container } = render(<CaptionBar text={null} visible={true} />);
expect(container.firstChild).toBeNull();
});

it('renders nothing when text is empty', () => {
const { container } = render(<CaptionBar text="" visible={true} />);
expect(container.firstChild).toBeNull();
});
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CaptionBar implementation includes a 3s hide timer when visible becomes false, but the tests only cover basic render/null/empty cases. Add a test using fake timers to assert the caption remains mounted during the fade period and is removed after ~3000ms (and that the timeout is cleared when visibility flips back).

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +7
// Renderer tests need jsdom
// @vitest-environment jsdom
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The // @vitest-environment jsdom directive needs to be the very first statement in the file (before any imports) for Vitest to apply it. As written, this test file will run under the default node environment and React Testing Library DOM APIs will fail.

Copilot uses AI. Check for mistakes.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 854f6882-24bb-489a-9487-6e696c5b5f67

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-implement-sprint-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots
0.0% Coverage on New Code (required ≥ 80%)
D Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@don-petry don-petry changed the title Implement TalkTerm: All 12 epics across 4 sprints feat: Epic 13 (Structured Questions) + Epic 14 (Native STT) Mar 30, 2026
Comment on lines +9 to +26
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci --legacy-peer-deps
- run: npm run make
- uses: actions/upload-artifact@v4
with:
name: talkterm-${{ matrix.os }}
path: out/make/**/*

release:
@github-actions
Copy link
Copy Markdown
Contributor

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved manually.

Please resolve the conflicts and push:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry
Copy link
Copy Markdown
Contributor Author

@claude Please address all open review comments on this PR from CodeRabbit and Copilot.

@claude
Copy link
Copy Markdown

claude Bot commented May 6, 2026

Claude finished @don-petry's task in 5m 30s —— View job


Addressing Open Review Comments

Finding: All 7 Copilot review items were already addressed in prior commits (the PR has 36 commits made after the review was posted). One gap remained: ActionPanel.tsx had keyboard navigation logic added in response to the review, but ActionPanel.test.tsx had no tests for those branches.

Tasks

  • Gather context and read relevant files
  • Verify admin-check.test.tsvi.unstubAllGlobals() already in afterEach
  • Verify ipc-message.tschannel already typed as IPCChannel
  • Verify agent-error.ts — TTS check already before STT ✓
  • Verify agent-event.tsRecoveryOption already imported from agent-error.ts (no duplication) ✓
  • Verify ActionPanel.tsx — keyboard nav (ArrowDown/Up, Enter, Space) already implemented ✓
  • Verify CaptionBar.test.tsx — fake timer tests for 3s hide already present ✓
  • Verify AdminBlockScreen.test.tsx@vitest-environment jsdom already on first line ✓
  • Add keyboard nav tests to ActionPanel.test.tsx — committed dddc998
  • Push to remote branch

What was committed (dddc998):
Added 10 new tests to src/renderer/components/overlay/ActionPanel.test.tsx covering:

  • ArrowDown moves focus to next enabled card
  • ArrowUp moves focus to previous enabled card
  • ArrowDown wraps from last to first
  • ArrowUp wraps from first to last
  • Enter selects the focused card
  • Space selects the focused card
  • Disabled cards are skipped during navigation
  • Enter does not fire onSelect when focused card is disabled
  • All-disabled no-op (no throw)
  • aria-label prop and default title-as-accessible-name

Cannot address (GitHub App permissions restrict .github/workflows/ edits):

  • ci.yml — expand push trigger to all branches
  • release.yml — add GitHub Release creation step + explicit permissions block
    | Branch

@don-petry don-petry requested a review from a team as a code owner May 6, 2026 11:37
@don-petry don-petry enabled auto-merge (squash) May 12, 2026 01:41
Root and others added 17 commits May 13, 2026 12:16
…olchain

- Scaffold Electron Forge + Vite + TypeScript project structure
- Install React 19, Rive WebGL2, Tailwind CSS with design tokens
- Configure strict TypeScript (noUncheckedIndexedAccess, exactOptionalPropertyTypes)
- Set up ESLint flat config, Prettier, Husky + lint-staged pre-commit
- Configure Vitest with coverage thresholds (90%)
- Create architecture-compliant src/ structure: main/, renderer/, shared/types/, preload/
- Forge config with DMG (macOS) and Squirrel (Windows) makers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ch state

Stories 2.1-2.6 complete:
- API key entry with validation states, masking, help link (2.1)
- User profile setup with name input (2.2)
- Avatar selection screen with MVP persona (2.3)
- Workspace selection with browse/skip paths (2.4)
- Launch state assessment with determineFirstIncompleteStep (2.5)
- Design tokens already in tailwind.config.ts from Story 1.1 (2.6)

Domain types: branded value objects (SessionId, WorkspacePath, ApiKey),
UserProfile, AvatarPersona, ApiKeyState, LaunchState

53 tests passing across 11 test files, all lint and typecheck clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Epic 3 — Avatar Presence & Voice Interaction:
- SpeechToText/TextToSpeech port interfaces in shared/types/ports
- WebSpeechStt and WebSpeechTts implementations (architectural boundary)
- useAvatarState hook with reducer (ready/listening/thinking/speaking)
- CaptionBar with auto-fade, StatusIndicator with pulsing dot states
- TextInput with Enter-to-send, Shift+Enter newline, mic button
- BargeInController state machine (speaking→listening on voice detect)

Epic 4 — Agent Backbone & Conversation:
- AgentBackend port interface with AsyncIterable<AgentEvent> streaming
- AgentEvent discriminated union (text, tool-call, confirm-request, etc.)
- FakeAgentBackend test double for all non-SDK tests
- AgentMessageRouter for dispatching events to handlers
- IPCMessage<T> envelope type with createIPCMessage factory
- IPC_CHANNELS const with all namespace:verb channel definitions
- ElectronAPI type for preload bridge contract

100 tests passing, lint and typecheck clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Epic 5 — Decision Presentation:
- useLayoutState hook (conversation/decision-point/output-review/output-only)
- ActionCard with role=option, aria-label, disabled/preferred/selected states
- ActionPanel (240px, vertical card stack, context header)

Epic 6 — Rich Output Display Modes:
- useDisplayMode hook for auto-selecting display mode from content type
- TaskProgress with step status icons, progress bar, live counters
- DocumentView for rendered markdown with optional file path
- ComparisonTable with color-coded scores, expandable rows
- ClusteredCards with expandable category groups, count badges, priority tags
- ActivityFeed (hidden by default, streaming action log)

Epic 7 — Error Handling:
- classifyError() for 8 error categories
- createUserFriendlyMessage() — no technical details ever

Epic 8/9 — Sessions & Audit:
- Session domain type with createSession factory
- AuditEntry domain type with createAuditEntry factory

149 tests passing, lint and typecheck clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Epic 10 — Preference Memory:
- Preference domain type with threshold constants
- shouldEstablishPreference (3 consistent) / shouldShiftPreference (2 different)

Epic 11 — External System Writeback:
- WritebackTarget discriminated union (local-file, pull-request, ado-work-item, mcp-system)
- SessionOrigin type and getDefaultWritebackType for contextual defaults

Epic 12 — Packaging & CI/CD:
- GitHub Actions CI workflow (typecheck, lint, format, test, coverage) on 3-OS matrix
- GitHub Actions release workflow for version tag builds

155 tests passing across 36 test files, lint and typecheck clean.
Full project: 12 epics implemented across 4 sprints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SQLite Persistence:
- Database/Statement/RunResult interfaces abstracting better-sqlite3
- Schema DDL: sessions, audit_entries, memory_index tables with indexes
- SqliteSessionRepository (save, findById, findIncomplete, updateStatus)
- SqliteAuditRepository (append, findBySession, findByDateRange)
- MemoryIndexStore (save, findBySession, findByType, findRecent)

Electron Adapters:
- ConfigStore interface + InMemoryConfigStore
- SafeStorageKeyManager with KeyManager/SafeStorageAPI interfaces
- Full encryption lifecycle: store, retrieve, delete, hasKey

IPC Layer:
- IPCMain/IPCRegistrar interfaces
- SettingsIPCHandler (key state, store key, profile CRUD, avatar select)
- SessionIPCHandler (start, resume, launch state assessment)
- Full preload bridge with contextBridge.exposeInMainWorld

App Routing:
- useSetupRouter hook (step progression through setup flow)
- App.tsx routes through needs-key → needs-profile → needs-avatar → needs-workspace → ready
- SessionGreeting component (resume/start new)
- AuditLog viewer with outcome filtering

214 tests across 48 files, lint and typecheck clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prettier was scanning _bmad/, _bmad-output/, and .claude/skills/
directories containing YAML, HTML, and JSON files from the BMAD
framework and Figma exports. These non-source artifacts don't need
formatting enforcement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P0 — Core Wiring:
- ClaudeSdkBackend with dynamic SDK import and demo mode fallback
- main.ts composition root: full DI wiring (database, repos, key manager,
  agent backend, IPC handlers, window creation, crash-safe persistence)
- Anti-corruption layer: SDK messages → domain AgentEvent mapping boundary

P1 — Layout & UI Completion:
- AppLayout three-zone component (left 240px, center flex, right 380px)
  with CSS transitions and responsive overlay mode
- OutputPanel shell dispatching to all 6 display modes
- PlanPreview display mode (numbered steps + approach summary)
- ConfirmPlan component (Approve/Modify/Cancel action cards)
- ErrorRecovery component (dynamic recovery cards from classified errors)
- FileDropZone with drag-and-drop visual feedback
- useNetworkStatus hook (online/offline detection)

247 tests across 56 files, lint and typecheck clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add stryker.config.mjs for mutation testing (80% break threshold)
- Update sprint-status.yaml: all 45 stories and 12 epics marked done

Final stats: 61 source files, 56 test files, 247 tests
All quality gates passing (lint, typecheck, tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Native Package Integration:
- Install better-sqlite3 with WAL mode for real SQLite persistence
- Install electron-store for persistent config (swap pending)
- Install @anthropic-ai/claude-agent-sdk — dynamic import now succeeds
- Wire real safeStorage for API key encryption in composition root
- main.ts: real better-sqlite3 database at app.getPath('userData')/talkterm.db

CI Fix:
- Add .gitattributes with `* text=auto eol=lf` to normalize line endings
- Add endOfLine: "lf" to .prettierrc
- This fixes the Windows CI failure where CRLF checkout caused 145 Prettier warnings

246 tests passing, lint and typecheck clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avatar:
- AvatarCanvas with placeholder (emoji states) and Rive canvas path
- assets/avatars/ directory with README documenting CC BY 4.0 source
- State machine ready for .riv integration (isListening/isThinking/isSpeaking)

E2E Tests:
- Playwright config targeting test/e2e/ with Vite dev server
- Setup flow tests (API key screen, disabled button, help link)
- Accessibility tests (heading structure, keyboard navigation)
- Design token tests (dark background, Inter font)

250 unit tests + 7 E2E specs ready to run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configure vite.renderer.config.ts to:
- Set optimizeDeps.entries to only scan src/renderer/
- Ignore _bmad/ and _bmad-output/ in file watcher

This prevents Vite's dep scanner from treating Figma screen
exports and UX design HTML files as application entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Install electron-squirrel-startup (was missing, caused crash on launch)
- Fix Vite optimizeDeps to only scan index.html (not test files)
- Remove debug screenshot code from main.ts
- Remove disableHardwareAcceleration (headless-only workaround)

App launches successfully: dark stage background renders, React mounts,
setup flow active. Verified via Electron internal capturePage API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix vi.stubGlobal leak in admin-check tests (add unstubAllGlobals)
- Type IPC message channel as IPCChannel instead of string
- Expand CI push trigger to all branches
- Add GitHub Release step to release workflow
- Fix classifyError TTS/STT ordering (speech synthesis → tts-error)
- Deduplicate RecoveryOption type (import from agent-error)
- Add keyboard navigation to ActionPanel listbox (arrow keys, Enter/Space)
- Add CaptionBar timer tests (3s hide delay)
- Move @vitest-environment directive to first line in AdminBlockScreen test
- Fix SonarCloud hotspots: bounded regex, safe temp dirs
- Fix deprecated FormEvent → SyntheticEvent
- Fix conditional useCallback hook in ApiKeySetup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Expand E2E setup-flow.spec.ts: full journey through API key → profile →
  avatar → workspace → greeting (FR40, FR36, FR1, FR5, FR52, FR53, FR33)
- Add accessibility.spec.ts: keyboard nav, ARIA semantics, color/text
  distinction, screen reader patterns (17 tests)
- Add integration tests: session lifecycle (FR30-32), audit trail (FR27-29),
  agent message flow (FR10-15), error pipeline (FR34-35) — 50 tests
- Fix formatting on all modified files
- Add test-results/ to .gitignore and .prettierignore

328 tests passing across 62 test files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add .coderabbit.yaml to .prettierignore (exists on main merge base)
- Pin softprops/action-gh-release to full SHA (SonarCloud hotspot)
- Add sonar-project.properties to exclude CI workflows from analysis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug 1: Browse Folder now shows a real folder picker via <input webkitdirectory>
  instead of immediately advancing. Shows selected folder confirmation.

Bug 2: Microphone button now wires WebSpeechStt — captures speech,
  shows interim transcripts, populates text input on final result.

Bug 3: Demo agent responses rotate through contextual messages instead
  of static "Agent integration coming soon!" placeholder.

Bug 4: TTS now speaks responses aloud via WebSpeechTts. Avatar returns
  to ready state when speech finishes.

Bug 5: Avatar placeholder now visible on greeting screen and enlarged
  (250x250, glow effect) for visual prominence across all screens.

341 tests passing across 62 files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 4, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
11 Security Hotspots
0.0% Coverage on New Code (required ≥ 80%)
4.6% Duplication on New Code (required ≤ 3%)
D Security Rating on New Code (required ≥ A)
D Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE


const backend = new ClaudeSdkBackend(createMockAuditRepo(), () => 'sk-ant-test-key');
const events = await collectEvents(
backend.startSession({ workspacePath: '/tmp/project', apiKey: 'sk-ant-test-key' }),
{ prompt: string; options: Record<string, unknown> },
];
expect(callArgs[0].options).toMatchObject({
cwd: '/tmp/project',
task_id: 't1',
status: 'completed',
summary: 'All tests passed',
output_file: '/tmp/out',
task_id: 't1',
status: 'failed',
summary: 'Build failed',
output_file: '/tmp/out',
() => memoryContext,
);
await collectEvents(
backend.startSession({ workspacePath: '/tmp/proj', apiKey: 'sk-ant-test-key' }),
() => null,
);
await collectEvents(
backend.startSession({ workspacePath: '/tmp/proj', apiKey: 'sk-ant-test-key' }),

describe('session handler roundtrips', () => {
it('session:start creates a session and returns its id', async () => {
const sessionId = await invoke(IPC_CHANNELS.SESSION_START, '/tmp/workspace');
expect(sessionId).toBeTruthy();
expect(sessionRepo.save).toHaveBeenCalledOnce();
const savedSession = vi.mocked(sessionRepo.save).mock.calls[0]![0] as Session;
expect(savedSession.workspacePath).toBe('/tmp/workspace');
const fakeSession: Session = {
id: 'test-session-id',
sdkSessionId: null,
workspacePath: '/tmp/ws',

it('launch:assess-state returns setup status', async () => {
configStore.set('userProfile', { name: 'Alice', avatarPersonaId: 'mary' });
configStore.set('workspacePath', '/tmp/ws');
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 4, 2026

CI Failure: SonarCloud Code Analysis

Step: Quality Gate evaluation
Root cause: Lint/style

SonarCloud Quality Gate failed on 5 conditions for the new code introduced in this PR: 11 Security Hotspots (driving a D Security Rating), D Reliability Rating, 0.0% coverage on new code (required ≥ 80%), and 4.6% duplication (required ≤ 3%). The security hotspots likely stem from the new sherpa-onnx-stt.ts, speech-ipc-handler.ts, and IPC audio pipeline code, which SonarCloud flagged for unsafe patterns. The 0% coverage reading indicates that although 78 unit tests exist locally, coverage reports are not being uploaded to SonarCloud during the CI run.

Suggested fix: Add a sonar.javascript.lcov.reportPaths (or equivalent sonar.coverage.jacoco.xmlReportPaths) entry to sonar-project.properties pointing to the Vitest LCOV output, so coverage data is uploaded; then review and address the 11 Security Hotspots flagged at SonarCloud Security Hotspots.

View run logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human-review Requires human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants