# Review package Task 2

## Commits
6c12d4b feat: add project workspace data model

## Stat
 .superpowers/sdd/task-2-report.md                | 44 +++++++++++
 apps/web/src/features/workbench/mockData.test.ts | 29 ++++++++
 apps/web/src/features/workbench/mockData.ts      | 61 ++++++++++++++++
 apps/web/src/features/workbench/types.ts         | 93 ++++++++++++++++++++++++
 4 files changed, 227 insertions(+)

## Diff
diff --git a/.superpowers/sdd/task-2-report.md b/.superpowers/sdd/task-2-report.md
new file mode 100644
index 0000000..48a37cf
--- /dev/null
+++ b/.superpowers/sdd/task-2-report.md
@@ -0,0 +1,44 @@
+# Task 2 Report: Workbench Types And Project-Scoped Mock Data
+
+## What changed
+
+- Added the typed workbench domain model in `apps/web/src/features/workbench/types.ts`.
+- Added two project records and project-scoped workspace mock data in `apps/web/src/features/workbench/mockData.ts`.
+- Added coverage for dynamic projects, project-specific channels, channel ordering, and custom-link URL shape in `apps/web/src/features/workbench/mockData.test.ts`.
+- Kept the implementation scoped to the requested workbench data and test files. No UI components, React dependencies, or shadcn/ui dependencies were changed.
+
+## Tests
+
+- Targeted test: `npm test -- --run src/features/workbench/mockData.test.ts` -> 1 file passed, 2 tests passed.
+- Full Web test suite: `npm test -- --run` -> 4 files passed, 7 tests passed.
+- Web production build: `npm run build` -> successful.
+- Formatting/diff validation: `git diff --check` -> no issues.
+
+## TDD evidence
+
+1. Created `mockData.test.ts` before production implementation.
+2. Ran the targeted test and observed the expected red result: Vitest could not resolve `./mockData` because the implementation files did not exist.
+3. Added `types.ts` and `mockData.ts` using the exact task brief values.
+4. Re-ran the targeted test and observed green: 2/2 tests passed.
+5. Ran the full Web test suite and build successfully.
+
+## Files changed
+
+- `apps/web/src/features/workbench/types.ts`
+- `apps/web/src/features/workbench/mockData.ts`
+- `apps/web/src/features/workbench/mockData.test.ts`
+- `.superpowers/sdd/task-2-report.md`
+
+## Self-review
+
+- `ChannelType` contains all seven required channel literals in the specified order.
+- `ProjectWorkspace` includes all required project-scoped collections.
+- Both projects receive distinct channel IDs, item IDs, and custom-link URLs through the project ID interpolation.
+- `getProjectWorkspace` returns the requested workspace and preserves the brief's fallback to the first workspace for unknown IDs.
+- The test verifies at least two projects, numeric IDs, names, distinct workspace IDs, channel order, and custom-link URL format.
+- No unrelated tracked files were changed.
+
+## Concerns
+
+- Vitest and Vite report the pre-existing warning that no Svelte config was found in `apps/web`; it did not prevent tests or the build from succeeding.
+- The mock data is intentionally static and duplicated across projects via the shared workspace mapper, as required for this MVP task.
diff --git a/apps/web/src/features/workbench/mockData.test.ts b/apps/web/src/features/workbench/mockData.test.ts
new file mode 100644
index 0000000..fdbf451
--- /dev/null
+++ b/apps/web/src/features/workbench/mockData.test.ts
@@ -0,0 +1,29 @@
+import { describe, expect, it } from 'vitest';
+import { getProjectWorkspace, workbenchProjects } from './mockData';
+
+describe('workbench mock data', () => {
+  it('provides dynamic projects', () => {
+    expect(workbenchProjects.length).toBeGreaterThanOrEqual(2);
+    expect(workbenchProjects[0]).toMatchObject({ id: expect.any(Number), name: expect.any(String) });
+  });
+
+  it('loads project-specific channels and counts', () => {
+    const first = getProjectWorkspace(workbenchProjects[0].id);
+    const second = getProjectWorkspace(workbenchProjects[1].id);
+
+    expect(first.project.id).not.toBe(second.project.id);
+    expect(first.channels.map((channel) => channel.type)).toEqual([
+      'overview',
+      'inbox',
+      'tasks',
+      'ai_sessions',
+      'notes_sources',
+      'cron',
+      'custom_link',
+    ]);
+    expect(first.channels.find((channel) => channel.type === 'custom_link')).toMatchObject({
+      title: expect.any(String),
+      url: expect.stringMatching(/^https?:\/\//),
+    });
+  });
+});
diff --git a/apps/web/src/features/workbench/mockData.ts b/apps/web/src/features/workbench/mockData.ts
new file mode 100644
index 0000000..90a25c2
--- /dev/null
+++ b/apps/web/src/features/workbench/mockData.ts
@@ -0,0 +1,61 @@
+import type { ProjectWorkspace, WorkbenchChannel, WorkbenchProject } from './types';
+
+export const workbenchProjects: WorkbenchProject[] = [
+  { id: 1, name: 'Project A1', description: 'Client strategy workspace', initials: 'A1', unreadCount: 36 },
+  { id: 2, name: 'Project A2', description: 'Operations planning workspace', initials: 'A2', unreadCount: 12 },
+];
+
+function systemChannels(projectID: number): WorkbenchChannel[] {
+  return [
+    { id: `${projectID}-overview`, projectID, type: 'overview', title: 'Overview', icon: 'home', count: 635, sortOrder: 1 },
+    { id: `${projectID}-inbox`, projectID, type: 'inbox', title: 'Message Flow', icon: 'mail', count: 36, sortOrder: 2 },
+    { id: `${projectID}-tasks`, projectID, type: 'tasks', title: 'Work Plan', icon: 'list', count: 8, sortOrder: 3 },
+    { id: `${projectID}-ai`, projectID, type: 'ai_sessions', title: 'AI Sessions', icon: 'sparkles', count: 4, sortOrder: 4 },
+    { id: `${projectID}-notes`, projectID, type: 'notes_sources', title: 'Notes & Sources', icon: 'file', count: 343, sortOrder: 5 },
+    { id: `${projectID}-cron`, projectID, type: 'cron', title: 'Cron Plans', icon: 'clock', count: 45, sortOrder: 6 },
+    {
+      id: `${projectID}-custom-roadmap`,
+      projectID,
+      type: 'custom_link',
+      title: projectID === 1 ? 'Roadmap Board' : 'Ops Dashboard',
+      icon: 'link',
+      url: projectID === 1 ? 'https://example.com/roadmap-a1' : 'https://example.com/ops-a2',
+      sortOrder: 7,
+    },
+  ];
+}
+
+const workspaces: ProjectWorkspace[] = workbenchProjects.map((project) => ({
+  project,
+  channels: systemChannels(project.id),
+  tags: ['all', 'tag1', 'UI', 'knowledge'],
+  recentSessions: [
+    { id: `${project.id}-session-1`, title: 'Skill setup notes', summary: 'Install and verify Codex skills.', updatedAt: 'Yesterday', references: ['Notes'] },
+    { id: `${project.id}-session-2`, title: 'Pricing logic analysis', summary: 'Compare decision branches and risks.', updatedAt: '7 days ago', references: ['Tasks', 'Sources'] },
+  ],
+  inbox: [
+    { id: `${project.id}-inbox-1`, source: 'Manual', title: 'Collect competitor pricing notes', summary: 'Turn pasted research into structured follow-up tasks.', status: 'open', tag: 'UI', time: '09:32' },
+    { id: `${project.id}-inbox-2`, source: 'AI', title: 'Meeting summary candidate', summary: 'Review suggested note before saving official object.', status: 'processed', tag: 'knowledge', time: 'Yesterday' },
+  ],
+  tasks: [
+    { id: `${project.id}-task-1`, title: 'Confirm homepage information architecture', summary: 'Review channel layout and right inspector behavior.', completed: false, owner: 'David', due: 'Today', tag: 'UI' },
+    { id: `${project.id}-task-2`, title: 'Archive old planning notes', summary: 'Move outdated notes into processed state.', completed: true, owner: 'Team', due: 'Yesterday', tag: 'knowledge' },
+  ],
+  aiSessions: [
+    { id: `${project.id}-ai-1`, title: 'UI prototype critique', summary: 'Discuss Discord-like project channel behavior.', updatedAt: '10 min ago', references: ['ScreenShot.png', 'design.md'] },
+    { id: `${project.id}-ai-2`, title: 'Task sharing policy', summary: 'Validate explicit object sharing rules.', updatedAt: 'Yesterday', references: ['Tasks'] },
+  ],
+  notesSources: [
+    { id: `${project.id}-note-1`, kind: 'note', title: 'Product workbench principles', updatedAt: 'Today', tag: 'knowledge', source: 'Markdown' },
+    { id: `${project.id}-file-1`, kind: 'file', title: 'ScreenShot.png', updatedAt: 'Today', tag: 'UI', source: 'Attachment' },
+    { id: `${project.id}-link-1`, kind: 'link', title: 'Reference board', updatedAt: '7 days ago', tag: 'tag1', source: 'URL' },
+  ],
+  cronPlans: [
+    { id: `${project.id}-cron-1`, title: 'Weekly inbox review reminder', schedule: '0 9 * * 1', nextRun: 'Next Monday 09:00', enabled: true, lastResult: 'Not run yet', owner: 'David' },
+    { id: `${project.id}-cron-2`, title: 'Monthly source cleanup', schedule: '0 10 1 * *', nextRun: 'Next month', enabled: false, lastResult: 'Paused', owner: 'Team' },
+  ],
+}));
+
+export function getProjectWorkspace(projectID: number): ProjectWorkspace {
+  return workspaces.find((workspace) => workspace.project.id === projectID) ?? workspaces[0];
+}
diff --git a/apps/web/src/features/workbench/types.ts b/apps/web/src/features/workbench/types.ts
new file mode 100644
index 0000000..98ae0f9
--- /dev/null
+++ b/apps/web/src/features/workbench/types.ts
@@ -0,0 +1,93 @@
+export type ChannelType =
+  | 'overview'
+  | 'inbox'
+  | 'tasks'
+  | 'ai_sessions'
+  | 'notes_sources'
+  | 'cron'
+  | 'custom_link';
+
+export type WorkbenchProject = {
+  id: number;
+  name: string;
+  description: string;
+  initials: string;
+  unreadCount: number;
+};
+
+export type WorkbenchChannel = {
+  id: string;
+  projectID: number;
+  type: ChannelType;
+  title: string;
+  icon: string;
+  count?: number;
+  url?: string;
+  sortOrder: number;
+};
+
+export type InboxMessage = {
+  id: string;
+  source: string;
+  title: string;
+  summary: string;
+  status: 'open' | 'processed' | 'archived';
+  tag: string;
+  time: string;
+};
+
+export type WorkTask = {
+  id: string;
+  title: string;
+  summary: string;
+  completed: boolean;
+  owner: string;
+  due: string;
+  tag: string;
+};
+
+export type AISessionItem = {
+  id: string;
+  title: string;
+  summary: string;
+  updatedAt: string;
+  references: string[];
+};
+
+export type NoteSourceItem = {
+  id: string;
+  kind: 'note' | 'file' | 'link';
+  title: string;
+  updatedAt: string;
+  tag: string;
+  source: string;
+};
+
+export type CronPlan = {
+  id: string;
+  title: string;
+  schedule: string;
+  nextRun: string;
+  enabled: boolean;
+  lastResult: string;
+  owner: string;
+};
+
+export type InspectorItem = {
+  title: string;
+  type: string;
+  description: string;
+  properties: Array<{ label: string; value: string }>;
+};
+
+export type ProjectWorkspace = {
+  project: WorkbenchProject;
+  channels: WorkbenchChannel[];
+  tags: string[];
+  recentSessions: AISessionItem[];
+  inbox: InboxMessage[];
+  tasks: WorkTask[];
+  aiSessions: AISessionItem[];
+  notesSources: NoteSourceItem[];
+  cronPlans: CronPlan[];
+};
