# ZoneGaze Social Studio Foundation Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Ship the first installable Matrix shell for `ZoneGaze Social Studio` with reusable module boundaries, runtime persistence, and BYO connector-aware foundation UX.

**Architecture:** Add `ZoneGaze Social Studio` as a new installable Matrix extension, but keep the shell thin. Put reusable business rules and connector descriptors in Social Studio-owned packages, put runtime/browser-safe schemas in `flow/contracts`, and keep Matrix runtime integration in a small `flow/runtime/src/social-studio/` adapter layer that persists `social-studio.json` beside the existing workspace files.

**Tech Stack:** TypeScript, React, Zod, existing Matrix/Flow runtime tRPC APIs, `@zonegaze/cline-sdk`, existing Matrix plugin shell.

## Global Constraints

- Keep the feature as an installable Matrix plugin under `extensions/social-studio/`.
- Keep every major capability behind its own typed module boundary.
- Use only free, commercially safe, proprietary-safe dependencies; hosted services remain user-supplied optional connectors.
- Keep `@cline/sdk` access behind `@zonegaze/cline-sdk`.
- Do not require any hosted model or social-platform connector for the plugin shell to render.
- Persist plugin state in the existing workspace state directory beside `board.json`, `sessions.json`, `meta.json`, and `chat.json`.
- Use the canonical local QA route `http://127.0.0.1:4217/demo-website`.
- This document covers only the foundation slice. Later slices for media analysis, planning, publishing, analytics, and automation should get separate plan files.

---

### Task 1: Register The Social Studio Plugin In Matrix

**Files:**
- Create: `extensions/social-studio/README.md`
- Create: `extensions/social-studio/src/matrix.ts`
- Create: `extensions/social-studio/apps/web/src/ZoneGazeSocialStudioMatrixSurface.tsx`
- Modify: `flow/common/src/plugin-services.ts`
- Modify: `flow/contracts/src/zonegaze-products.ts`
- Modify: `flow/web/src/extensions/matrix-installed-plugins.ts`
- Modify: `flow/web/src/whispr/marketplace.ts`
- Modify: `flow/web/src/components/project-navigation-panel.tsx`
- Modify: `flow/web/src/components/extensions/matrix-plugin-surfaces.tsx`
- Modify: `flow/web/src/App.tsx`
- Test: `flow/web/src/extensions/matrix-installed-plugins.test.tsx`
- Test: `flow/web/src/whispr/marketplace.test.ts`
- Test: `flow/web/src/components/project-navigation-panel.test.tsx`
- Test: `flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx`

**Interfaces:**
- Produces: plugin id `"zonegaze-social-studio"`
- Produces: nav section `"social"`
- Produces: `ZoneGazeSocialStudioMatrixSurface(props: { activeProjectId: string | null; onOpenModelSourceSetup?: (sourceId: "cline-sdk" | "ollama" | "exo" | "custom-api") => void }): ReactElement`

- [ ] **Step 1: Write the failing registration tests**

```ts
// flow/web/src/whispr/marketplace.test.ts
it("includes the ZoneGaze Social Studio marketplace item", () => {
	expect(getMarketplaceItemById("zonegaze-social-studio")).toMatchObject({
		id: "zonegaze-social-studio",
		name: "ZoneGaze Social Studio",
		sourcePath: "extensions/social-studio/",
	});
});
```

```ts
// flow/web/src/extensions/matrix-installed-plugins.test.tsx
it("allows installing the Social Studio plugin", async () => {
	expect(MATRIX_INSTALLABLE_PLUGIN_IDS).toContain("zonegaze-social-studio");
});
```

```ts
// flow/web/src/components/project-navigation-panel.test.tsx
it("shows Social when the plugin is installed", () => {
	expect(PLUGIN_NAV_ITEM_BY_ID["zonegaze-social-studio"]).toMatchObject({
		id: "social",
		label: "Social",
	});
});
```

- [ ] **Step 2: Run the focused tests to verify they fail**

Run:

```bash
./node_modules/.bin/vitest run flow/web/src/whispr/marketplace.test.ts flow/web/src/extensions/matrix-installed-plugins.test.tsx flow/web/src/components/project-navigation-panel.test.tsx flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
```

Expected: FAIL with missing `zonegaze-social-studio` registration and missing plugin surface exports.

- [ ] **Step 3: Add the plugin catalog, installable id, nav item, and matrix surface**

```ts
// flow/common/src/plugin-services.ts
export type ZoneGazePluginId =
	| "zonegaze-flow"
	| "zonegaze-whispr"
	| "zonegaze-chat"
	| "zonegaze-joplin"
	| "zonegaze-code"
	| "zonegaze-git"
	| "open-design"
	| "exo"
	| "zonegaze-research"
	| "zonegaze-social-studio";

export type MatrixPluginCapability =
	| "dictation"
	| "project-context"
	| "chat"
	| "notes"
	| "file-workspace"
	| "git"
	| "design-artifact"
	| "local-model-runtime"
	| "deep-research"
	| "social-studio";
```

```ts
// extensions/social-studio/src/matrix.ts
export { ZoneGazeSocialStudioMatrixSurface } from "../apps/web/src/ZoneGazeSocialStudioMatrixSurface";
```

```tsx
// extensions/social-studio/apps/web/src/ZoneGazeSocialStudioMatrixSurface.tsx
import type { ReactElement } from "react";

export function ZoneGazeSocialStudioMatrixSurface(): ReactElement {
	return (
		<div className="flex min-h-0 flex-1 items-center justify-center bg-surface-0 p-6 text-text-secondary">
			Social Studio foundation is not wired yet.
		</div>
	);
}
```

- [ ] **Step 4: Run the focused tests again**

Run:

```bash
./node_modules/.bin/vitest run flow/web/src/whispr/marketplace.test.ts flow/web/src/extensions/matrix-installed-plugins.test.tsx flow/web/src/components/project-navigation-panel.test.tsx flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
```

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add flow/common/src/plugin-services.ts flow/contracts/src/zonegaze-products.ts flow/web/src/extensions/matrix-installed-plugins.ts flow/web/src/whispr/marketplace.ts flow/web/src/components/project-navigation-panel.tsx flow/web/src/components/extensions/matrix-plugin-surfaces.tsx flow/web/src/App.tsx extensions/social-studio/README.md extensions/social-studio/src/matrix.ts extensions/social-studio/apps/web/src/ZoneGazeSocialStudioMatrixSurface.tsx flow/web/src/extensions/matrix-installed-plugins.test.tsx flow/web/src/whispr/marketplace.test.ts flow/web/src/components/project-navigation-panel.test.tsx flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
git commit -m "feat: register social studio plugin shell"
```

### Task 2: Add Reusable Social Studio Contracts And Module Registries

**Files:**
- Create: `flow/contracts/src/social-studio-contracts.ts`
- Create: `flow/contracts/src/social-studio-contracts.test.ts`
- Modify: `flow/contracts/src/index.ts`
- Modify: `flow/contracts/src/api-contract.ts`
- Create: `extensions/social-studio/packages/domain/src/module-catalog.ts`
- Create: `extensions/social-studio/packages/domain/src/index.ts`
- Create: `extensions/social-studio/packages/connectors/src/index.ts`

**Interfaces:**
- Produces: `SocialStudioModuleId`
- Produces: `socialStudioWorkspaceStateSchema`
- Produces: `type SocialStudioWorkspaceState`
- Produces: `SOCIAL_STUDIO_MODULE_CATALOG`
- Produces: `SOCIAL_STUDIO_CONNECTOR_CATALOG`

- [ ] **Step 1: Write the failing contracts test**

```ts
// flow/contracts/src/social-studio-contracts.test.ts
import { describe, expect, it } from "vitest";
import { socialStudioWorkspaceStateSchema } from "./social-studio-contracts";

describe("socialStudioWorkspaceStateSchema", () => {
	it("parses a minimal workspace state", () => {
		const parsed = socialStudioWorkspaceStateSchema.parse({
			version: 1,
			projectId: "project-1",
			activeModuleId: "dashboard",
			objectives: [],
			connectorRefs: [],
			assetCollections: [],
			draftCalendars: [],
			updatedAt: 1,
		});
		expect(parsed.activeModuleId).toBe("dashboard");
	});
});
```

- [ ] **Step 2: Run the contracts tests to verify they fail**

Run:

```bash
./node_modules/.bin/vitest run flow/contracts/src/social-studio-contracts.test.ts
```

Expected: FAIL with missing `social-studio-contracts.ts`.

- [ ] **Step 3: Add the shared schemas and reusable registries**

```ts
// flow/contracts/src/social-studio-contracts.ts
import { z } from "zod";

export const socialStudioModuleIdSchema = z.enum([
	"dashboard",
	"ingest",
	"research",
	"media",
	"planner",
	"publishing",
	"analytics",
	"automation",
]);

export type SocialStudioModuleId = z.infer<typeof socialStudioModuleIdSchema>;

export const socialStudioWorkspaceStateSchema = z.object({
	version: z.literal(1),
	projectId: z.string().nullable(),
	activeModuleId: socialStudioModuleIdSchema,
	objectives: z.array(z.object({ id: z.string(), name: z.string(), market: z.string(), offer: z.string().nullable() })),
	connectorRefs: z.array(z.object({ id: z.string(), kind: z.enum(["model", "publish", "analytics"]), label: z.string(), source: z.enum(["built-in", "user-supplied"]), status: z.enum(["ready", "needs-setup", "disabled"]), providerSourceId: z.enum(["cline-sdk", "ollama", "exo", "custom-api"]).nullable(), capabilities: z.array(z.string()) })),
	assetCollections: z.array(z.object({ id: z.string(), name: z.string(), assetCount: z.number().int().nonnegative() })),
	draftCalendars: z.array(z.object({ id: z.string(), name: z.string(), itemCount: z.number().int().nonnegative() })),
	updatedAt: z.number().int(),
});

export type SocialStudioWorkspaceState = z.infer<typeof socialStudioWorkspaceStateSchema>;
```

```ts
// extensions/social-studio/packages/domain/src/module-catalog.ts
import type { SocialStudioModuleId } from "../../../../flow/contracts/src/social-studio-contracts";

export const SOCIAL_STUDIO_MODULE_CATALOG: ReadonlyArray<{
	id: SocialStudioModuleId;
	label: string;
	description: string;
}> = [
	{ id: "dashboard", label: "Dashboard", description: "Overview of assets, connectors, drafts, and schedules." },
	{ id: "ingest", label: "Ingest", description: "Bring in folders, files, references, and project context." },
	{ id: "research", label: "Research", description: "Market and trend research using saved evidence." },
	{ id: "media", label: "Media", description: "Transcription, OCR, clip metadata, and reusable media signals." },
	{ id: "planner", label: "Planner", description: "Content angles, briefs, calendars, and post plans." },
	{ id: "publishing", label: "Publishing", description: "Connector-aware publishing jobs and delivery status." },
	{ id: "analytics", label: "Analytics", description: "Metrics, performance summaries, and feedback loops." },
	{ id: "automation", label: "Automation", description: "Schedules, retries, approvals, and audit history." },
];
```

- [ ] **Step 4: Run the contracts tests again**

Run:

```bash
./node_modules/.bin/vitest run flow/contracts/src/social-studio-contracts.test.ts
./node_modules/.bin/tsc -p flow/contracts/tsconfig.json --noEmit
```

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add flow/contracts/src/social-studio-contracts.ts flow/contracts/src/social-studio-contracts.test.ts flow/contracts/src/index.ts flow/contracts/src/api-contract.ts extensions/social-studio/packages/domain/src/module-catalog.ts extensions/social-studio/packages/domain/src/index.ts extensions/social-studio/packages/connectors/src/index.ts
git commit -m "feat: add social studio contracts and module registries"
```

### Task 3: Add Runtime Persistence And Workspace-Scoped API Endpoints

**Files:**
- Create: `flow/runtime/src/social-studio/social-studio-store.ts`
- Create: `flow/runtime/src/social-studio/social-studio-service.ts`
- Create: `flow/runtime/src/social-studio/index.ts`
- Create: `test/runtime/social-studio/social-studio-service.test.ts`
- Modify: `flow/runtime/src/trpc/runtime-api.ts`

**Interfaces:**
- Consumes: `type SocialStudioWorkspaceState`
- Produces: `createSocialStudioStore(options: { workspaceDirectoryPath: string }): SocialStudioStore`
- Produces: `class SocialStudioService`
- Produces: runtime procedures `getSocialStudioWorkspace` and `saveSocialStudioWorkspace`

- [ ] **Step 1: Write the failing runtime test**

```ts
// test/runtime/social-studio/social-studio-service.test.ts
import { describe, expect, it } from "vitest";
import { createSocialStudioStore } from "../../../flow/runtime/src/social-studio/social-studio-store";

describe("Social Studio store", () => {
	it("loads an empty workspace state when social-studio.json is missing", async () => {
		const store = createSocialStudioStore({
			workspaceDirectoryPath: "/tmp/zonegaze-social-studio-test",
			now: () => 1,
		});
		const state = await store.load();
		expect(state.activeModuleId).toBe("dashboard");
	});
});
```

- [ ] **Step 2: Run the runtime test to verify it fails**

Run:

```bash
./node_modules/.bin/vitest run test/runtime/social-studio/social-studio-service.test.ts
```

Expected: FAIL with missing store/service files and missing tRPC procedures.

- [ ] **Step 3: Add the file-backed store and narrow runtime service**

```ts
// flow/runtime/src/social-studio/social-studio-store.ts
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { lockedFileSystem } from "../fs/locked-file-system";
import { socialStudioWorkspaceStateSchema, type SocialStudioWorkspaceState } from "../core/api-contract";

const SOCIAL_STUDIO_STATE_FILENAME = "social-studio.json";

function createEmptyState(now: number): SocialStudioWorkspaceState {
	return {
		version: 1,
		projectId: null,
		activeModuleId: "dashboard",
		objectives: [],
		connectorRefs: [],
		assetCollections: [],
		draftCalendars: [],
		updatedAt: now,
	};
}
```

```ts
// flow/runtime/src/social-studio/social-studio-service.ts
export class SocialStudioService {
	constructor(private readonly store: SocialStudioStore) {}

	async getWorkspace() {
		return await this.store.load();
	}

	async saveWorkspace(input: SocialStudioWorkspaceState) {
		return await this.store.save(input);
	}
}
```

- [ ] **Step 4: Run the runtime tests again**

Run:

```bash
./node_modules/.bin/vitest run test/runtime/social-studio/social-studio-service.test.ts
./node_modules/.bin/tsc -p tsconfig.json --noEmit
```

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add flow/runtime/src/social-studio/social-studio-store.ts flow/runtime/src/social-studio/social-studio-service.ts flow/runtime/src/social-studio/index.ts flow/runtime/src/trpc/runtime-api.ts test/runtime/social-studio/social-studio-service.test.ts
git commit -m "feat: add social studio runtime workspace service"
```

### Task 4: Build The Social Studio Matrix Surface And Workspace Hook

**Files:**
- Create: `extensions/social-studio/apps/web/src/use-social-studio-workspace.ts`
- Create: `extensions/social-studio/apps/web/src/social-studio-shell.tsx`
- Modify: `extensions/social-studio/apps/web/src/ZoneGazeSocialStudioMatrixSurface.tsx`
- Test: `flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx`

**Interfaces:**
- Consumes: runtime procedures `getSocialStudioWorkspace` and `saveSocialStudioWorkspace`
- Consumes: `SOCIAL_STUDIO_MODULE_CATALOG`
- Produces: `useSocialStudioWorkspace(input: { activeProjectId: string | null }): { state: SocialStudioWorkspaceState | null; loading: boolean; error: string | null; setActiveModuleId(next: SocialStudioModuleId): Promise<void>; }`

- [ ] **Step 1: Extend the plugin surface test with a visible Social Studio shell assertion**

```tsx
// flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
it("renders the Social Studio surface", () => {
	render(<ZoneGazeSocialStudioMatrixSurface activeProjectId={null} />);
	expect(screen.getByText("Social Studio")).toBeInTheDocument();
	expect(screen.getByText("BYO connectors")).toBeInTheDocument();
});
```

- [ ] **Step 2: Run the surface test to verify it fails**

Run:

```bash
./node_modules/.bin/vitest run flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
```

Expected: FAIL because the current placeholder surface does not render the expected shell.

- [ ] **Step 3: Replace the placeholder with a real foundation shell**

```tsx
// extensions/social-studio/apps/web/src/ZoneGazeSocialStudioMatrixSurface.tsx
import type { ReactElement } from "react";
import { useSocialStudioWorkspace } from "./use-social-studio-workspace";
import { SocialStudioShell } from "./social-studio-shell";

export function ZoneGazeSocialStudioMatrixSurface({
	activeProjectId,
	onOpenModelSourceSetup,
}: {
	activeProjectId: string | null;
	onOpenModelSourceSetup?: (sourceId: "cline-sdk" | "ollama" | "exo" | "custom-api") => void;
}): ReactElement {
	const workspace = useSocialStudioWorkspace({ activeProjectId });
	return <SocialStudioShell workspace={workspace} onOpenModelSourceSetup={onOpenModelSourceSetup} />;
}
```

```tsx
// extensions/social-studio/apps/web/src/social-studio-shell.tsx
export function SocialStudioShell({ workspace, onOpenModelSourceSetup }: SocialStudioShellProps): ReactElement {
	return (
		<div className="flex min-h-0 flex-1 flex-col bg-surface-0">
			<header className="border-b border-border bg-surface-1 px-4 py-3">
				<h1 className="m-0 text-base font-semibold text-text-primary">Social Studio</h1>
				<p className="m-0 mt-1 text-sm text-text-secondary">BYO connectors for models, research, publishing, and analytics.</p>
			</header>
		</div>
	);
}
```

- [ ] **Step 4: Run the surface test again**

Run:

```bash
./node_modules/.bin/vitest run flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
npm --prefix flow/web run typecheck
```

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add extensions/social-studio/apps/web/src/use-social-studio-workspace.ts extensions/social-studio/apps/web/src/social-studio-shell.tsx extensions/social-studio/apps/web/src/ZoneGazeSocialStudioMatrixSurface.tsx flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
git commit -m "feat: add social studio foundation surface"
```

### Task 5: Add BYO Connector UX, Docs, And Browser QA

**Files:**
- Modify: `extensions/social-studio/README.md`
- Modify: `extensions/social-studio/apps/web/src/social-studio-shell.tsx`
- Test: `flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx`

**Interfaces:**
- Consumes: `SOCIAL_STUDIO_CONNECTOR_CATALOG`
- Produces: connector CTA buttons that can open existing Matrix model-source setup for `cline-sdk`, `ollama`, `exo`, or `custom-api`

- [ ] **Step 1: Add a failing UI test for the BYO model CTA**

```tsx
// flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
it("offers a BYO model connector setup action", async () => {
	const onOpenModelSourceSetup = vi.fn();
	render(
		<ZoneGazeSocialStudioMatrixSurface
			activeProjectId={null}
			onOpenModelSourceSetup={onOpenModelSourceSetup}
		/>,
	);
	await user.click(screen.getByRole("button", { name: "Connect Cline SDK" }));
	expect(onOpenModelSourceSetup).toHaveBeenCalledWith("cline-sdk");
});
```

- [ ] **Step 2: Run the focused UI test to verify it fails**

Run:

```bash
./node_modules/.bin/vitest run flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
```

Expected: FAIL because the button and callback wiring do not exist yet.

- [ ] **Step 3: Add the BYO connector actions and document the foundation slice**

```tsx
// extensions/social-studio/apps/web/src/social-studio-shell.tsx
<Button
	variant="primary"
	size="sm"
	onClick={() => onOpenModelSourceSetup?.("cline-sdk")}
>
	Connect Cline SDK
</Button>
```

```md
<!-- extensions/social-studio/README.md -->
# ZoneGaze Social Studio

ZoneGaze Social Studio is the installable Matrix social-media operations plugin.

## Foundation Scope
- Matrix plugin registration
- Workspace-scoped `social-studio.json` persistence
- Reusable contracts and module registries
- BYO connector-aware shell for model, publishing, and analytics integrations
```

- [ ] **Step 4: Run validation and browser QA**

Run:

```bash
./node_modules/.bin/vitest run flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx test/runtime/social-studio/social-studio-service.test.ts flow/contracts/src/social-studio-contracts.test.ts
./node_modules/.bin/tsc -p tsconfig.json --noEmit
npm --prefix flow/web run typecheck
```

Then run the app and verify manually at:

```text
http://127.0.0.1:4217/demo-website
```

Expected manual result: Social Studio appears in Marketplace and the left rail after install, opens as a full-bleed plugin surface, shows the module shell, and the `Connect Cline SDK` button opens the existing provider-setup flow.

- [ ] **Step 5: Commit**

```bash
git add extensions/social-studio/README.md extensions/social-studio/apps/web/src/social-studio-shell.tsx flow/web/src/components/extensions/matrix-plugin-surfaces.test.tsx
git commit -m "docs: finish social studio foundation slice"
```

## Self-Review

- Spec coverage: this plan covers the first independently shippable slice only: plugin registration, modular contracts, runtime persistence, foundation UI, and BYO model connector entrypoints.
- Gaps intentionally deferred to later plans: asset intake, media analysis, trend research adapters, post planning, publishing connectors, analytics ingestion, and automation workers.
- Placeholder scan: no TODO/TBD markers remain.
- Type consistency: the plan uses one plugin id (`zonegaze-social-studio`) and one nav section (`social`) consistently across registration, runtime, and UI tasks.

## Execution Handoff

Plan complete and saved to `docs/superpowers/plans/2026-07-08-zonegaze-social-studio-foundation.md`. Two execution options:

**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration

**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints

**Which approach?**
