---
phase: 21-chat-foundation
plan: 00
type: execute
wave: 0
depends_on: []
files_modified:
  - server/src/__tests__/chat-service.test.ts
  - server/src/__tests__/chat-routes.test.ts
  - ui/src/components/ChatMarkdownMessage.test.tsx
  - ui/src/components/ChatInput.test.tsx
autonomous: true
requirements: [HIST-01, CHAT-02, CHAT-03, CHAT-04, CHAT-05, CHAT-06, INPUT-07]

must_haves:
  truths:
    - "Test stubs exist and can be executed by vitest without errors"
    - "Each stub has describe blocks with placeholder tests that skip or pass trivially"
  artifacts:
    - path: "server/src/__tests__/chat-service.test.ts"
      provides: "Test scaffold for chat service (HIST-01, CHAT-04, CHAT-05, CHAT-06)"
      contains: "describe.*chatService"
    - path: "server/src/__tests__/chat-routes.test.ts"
      provides: "Test scaffold for chat routes (POST conversation, GET list, POST message)"
      contains: "describe.*chatRoutes"
    - path: "ui/src/components/ChatMarkdownMessage.test.tsx"
      provides: "Test scaffold for markdown rendering (CHAT-02, CHAT-03)"
      contains: "describe.*ChatMarkdownMessage"
    - path: "ui/src/components/ChatInput.test.tsx"
      provides: "Test scaffold for keyboard shortcuts (INPUT-07)"
      contains: "describe.*ChatInput"
  key_links: []
---

<objective>
Create Wave 0 test stubs for the four key test files needed by Plans 01-05.

Purpose: Satisfy the Nyquist rule — every implementation task must have a pre-existing test file with describe blocks and placeholder expectations. Plans 01 and 02 depend on these stubs existing before they execute.
Output: Four test files with describe/it blocks that vitest can discover and run.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/21-chat-foundation/21-RESEARCH.md

<interfaces>
From server/src/__tests__/activity-routes.test.ts (reference pattern for server tests):
```typescript
import express from "express";
import request from "supertest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { errorHandler } from "../middleware/index.js";

const mockService = vi.hoisted(() => ({ list: vi.fn(), create: vi.fn() }));
vi.mock("../services/activity.js", () => ({ activityService: () => mockService }));

function createApp() {
  const app = express();
  app.use(express.json());
  // ... mock actor middleware
}
```

From ui/src/components/MarkdownBody.test.tsx (reference pattern for UI component tests):
```typescript
// @vitest-environment node
import { describe, expect, it } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { ThemeProvider } from "../context/ThemeContext";
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create server-side test stubs (chat-service + chat-routes)</name>
  <files>server/src/__tests__/chat-service.test.ts, server/src/__tests__/chat-routes.test.ts</files>
  <read_first>
    - server/src/__tests__/activity-routes.test.ts (full file — reference for mock pattern, createApp, supertest usage)
  </read_first>
  <action>
Create `server/src/__tests__/chat-service.test.ts`:

```typescript
import { describe, it, expect } from "vitest";

describe("chatService", () => {
  describe("createConversation", () => {
    it.todo("creates a conversation row with companyId");
    it.todo("returns the created conversation with id and timestamps");
  });

  describe("listConversations", () => {
    it.todo("returns conversations sorted by updatedAt DESC");
    it.todo("excludes soft-deleted conversations");
    it.todo("supports cursor-based pagination with hasMore");
    it.todo("limits results to max 100");
  });

  describe("getConversation", () => {
    it.todo("returns conversation by id");
    it.todo("throws notFound for non-existent conversation");
    it.todo("throws notFound for soft-deleted conversation");
  });

  describe("updateConversation", () => {
    it.todo("updates title");
    it.todo("sets pinnedAt timestamp");
    it.todo("clears pinnedAt when set to null");
    it.todo("sets archivedAt timestamp");
    it.todo("bumps updatedAt on every update");
  });

  describe("softDeleteConversation", () => {
    it.todo("sets deletedAt timestamp");
    it.todo("throws notFound if already deleted");
  });

  describe("addMessage", () => {
    it.todo("inserts a message row with conversationId and role");
    it.todo("bumps conversation updatedAt after insert");
    it.todo("auto-sets title from first user message when title is null");
    it.todo("does not overwrite existing title on subsequent messages");
  });

  describe("listMessages", () => {
    it.todo("returns messages for conversation sorted by createdAt DESC");
    it.todo("supports cursor-based pagination");
  });
});
```

Create `server/src/__tests__/chat-routes.test.ts`:

```typescript
import { describe, it, expect } from "vitest";

describe("chatRoutes", () => {
  describe("POST /companies/:companyId/conversations", () => {
    it.todo("creates a conversation and returns 201");
    it.todo("accepts optional title and agentId");
  });

  describe("GET /companies/:companyId/conversations", () => {
    it.todo("returns paginated conversation list");
    it.todo("supports cursor query param");
  });

  describe("GET /conversations/:id", () => {
    it.todo("returns conversation by id");
    it.todo("returns 404 for non-existent conversation");
  });

  describe("PATCH /conversations/:id", () => {
    it.todo("updates conversation fields");
  });

  describe("DELETE /conversations/:id", () => {
    it.todo("soft-deletes and returns 204");
  });

  describe("POST /conversations/:id/messages", () => {
    it.todo("creates a message and returns 201");
    it.todo("rejects invalid role");
  });

  describe("GET /conversations/:id/messages", () => {
    it.todo("returns paginated message list");
  });
});
```

Both files use `it.todo()` which vitest marks as skipped — they run without error and serve as scaffolds for implementation tasks to fill in.
  </action>
  <verify>
    <automated>cd /opt/nexus && pnpm vitest run server/src/__tests__/chat-service.test.ts server/src/__tests__/chat-routes.test.ts --reporter=verbose 2>&1 | tail -5</automated>
  </verify>
  <done>Server test stubs exist with describe/it.todo blocks covering all chatService methods and chatRoutes endpoints. Vitest runs them without error.</done>
</task>

<task type="auto">
  <name>Task 2: Create UI test stubs (ChatMarkdownMessage + ChatInput)</name>
  <files>ui/src/components/ChatMarkdownMessage.test.tsx, ui/src/components/ChatInput.test.tsx</files>
  <read_first>
    - ui/src/components/MarkdownBody.test.tsx (reference for UI component test pattern — renderToStaticMarkup, ThemeProvider wrapper)
    - ui/src/components/IssueRow.test.tsx (reference for component test with RTL if used)
  </read_first>
  <action>
Create `ui/src/components/ChatMarkdownMessage.test.tsx`:

```tsx
// @vitest-environment node
import { describe, it, expect } from "vitest";

describe("ChatMarkdownMessage", () => {
  describe("markdown rendering (CHAT-02)", () => {
    it.todo("renders plain text as paragraph");
    it.todo("renders code blocks with hljs classes for syntax highlighting");
    it.todo("renders GFM tables");
    it.todo("renders headings, lists, and links");
  });

  describe("code block features (CHAT-03)", () => {
    it.todo("renders language label from code fence");
    it.todo("renders copy button with aria-label");
    it.todo("extracts code text content for clipboard");
  });
});
```

Create `ui/src/components/ChatInput.test.tsx`:

```tsx
// @vitest-environment node
import { describe, it, expect } from "vitest";

describe("ChatInput", () => {
  describe("keyboard shortcuts (INPUT-07)", () => {
    it.todo("calls onSend when Enter is pressed without Shift");
    it.todo("inserts newline when Shift+Enter is pressed");
    it.todo("clears input when Escape is pressed");
    it.todo("does not send when input is empty");
  });

  describe("auto-resize (INPUT-01)", () => {
    it.todo("textarea has max-height constraint");
  });

  describe("submit state", () => {
    it.todo("disables send button when isSubmitting is true");
  });
});
```

Both files use `it.todo()` for the same reason as the server stubs.
  </action>
  <verify>
    <automated>cd /opt/nexus && pnpm vitest run ui/src/components/ChatMarkdownMessage.test.tsx ui/src/components/ChatInput.test.tsx --reporter=verbose 2>&1 | tail -5</automated>
  </verify>
  <done>UI test stubs exist with describe/it.todo blocks covering ChatMarkdownMessage rendering and ChatInput keyboard shortcuts. Vitest runs them without error.</done>
</task>

</tasks>

<verification>
- All four test files exist and vitest discovers them
- `pnpm vitest run server/src/__tests__/chat-service.test.ts` exits 0
- `pnpm vitest run server/src/__tests__/chat-routes.test.ts` exits 0
- `pnpm vitest run ui/src/components/ChatMarkdownMessage.test.tsx` exits 0
- `pnpm vitest run ui/src/components/ChatInput.test.tsx` exits 0
</verification>

<success_criteria>
- Four test stub files created with describe blocks matching the requirements they cover
- Vitest runs all four files without errors (todo tests are skipped, not failed)
- Implementation plans (01-05) can reference these files in their verify commands
</success_criteria>

<output>
After completion, create `.planning/phases/21-chat-foundation/21-00-SUMMARY.md`
</output>
