Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/plans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This directory is the source of truth for Aiden's implementation plans. The engi
| [Compaction](compaction-plan.md) | Partial | Pi-native checkpoints, lifecycle/crash recovery, and exact audited-upstream compatibility ship; durable memory and provider-native paths remain open. |
| [Dynamic Model Catalog](dynamic-model-catalog-plan.md) | Implemented | Validated pi.dev overlays, offline `0600` cache hydration, scoped setup refresh, four-hour launch refresh, force refresh, Pi metadata fallback, and Mac/iOS projection ship on pinned Pi 0.84.4. |
| [Generative UI Artifacts](generative-ui-artifacts-plan.md) | Active | Phases 0–6 shipped: chat-scoped `render_artifact`, strict sandboxed preview/export hosts, verified vendored Chart.js/Plotly/KaTeX, permission-aware `/visualize`, crash-recoverable authoritative storage/copies, descriptor-relative workspace reads, one-iframe handoff/expansion, visible failure states, and route-stable Responding/Visualizing activity. Three-agent PR review findings are remediated with focused regression coverage. |
| [Stitch-Inspired Design Studio](stitch-inspired-design-studio-plan.md) | Active | Phase 1 is complete: selection, exact revision navigation, canvas recovery, inspector layout, workflow terminology, and responsive Electron acceptance are green. Phase 2 is in progress: Project V2, durable title policy, per-Screen surface semantics, and migration. |
| [Generation Progress Notes](generation-progress-notes-plan.md) | Planned | No implementation yet. |
| [Logging and Diagnostics Upgrade](logging-and-diagnostics-upgrade-plan.md) | Implemented | Phases 0–7 are implemented: bounded typed desktop journals, main-owned renderer evidence, local support export/delete, native categorical parity, and CI/release gates. Signed/notarized `v0.35.0` passed packaged diagnostics acceptance; physical-device termination receipts remain. |
| [Long-thread payload upgrades](long-thread-payload-upgrade-plan.md) | Partial | Investigation complete: T3’s O(N²) stdout store does not exist here. No-op `toolRunning` timeline republish is skipped; Remote gzip, stream-journal debounce, chat JSON/attachments, and transcript windowing remain planned. |
Expand Down
130 changes: 130 additions & 0 deletions docs/plans/stitch-inspired-design-studio-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Stitch-Inspired Design Studio Plan

Status: Active — Phases 1–2 complete; Phase 3 implementation in progress
Date: 2026-09-03
Branch: `feature/stitch-design-studio`
Predecessor: [Design Workspace Durable Projects and Handoff](completed/design-workspace-claude-alignment-plan.md)
Research notes: [Stitch product and interaction findings](../../stitch-ideas.md)

## Objective

Evolve Aiden Design from a conversation-attached canvas into a local-first, screen-centered studio:

```text
Brief → Explore → Choose → Refine → Prototype → Inspect → Export / Build
```

Preserve Aiden's main-owned storage, immutable revisions, semantic compare-and-swap publication, restart reconciliation, sandboxed previews, deterministic export, and permission-preserving engineering handoff. Conversation remains a contextual work surface rather than the product's organizing center.

## Delivery status

- [x] Phase 1 — coherent selection, canvas recovery, inspector layout, terminology, and responsive workbench
- [x] Phase 2 — Project V2, title policy, per-screen surface semantics, and migration
- [ ] Phase 3 — durable generation intents, Explore, single-screen Refine, direction sets, and cancellation recovery
- [ ] Phase 4 — project-local Design Language and hardened deterministic `DESIGN.md`
- [ ] Phase 5 — bounded prototype graph and host verification
- [ ] Phase 6 — project export and handoff V2
- [ ] Final — combined review, full CI, packaged acceptance, and PR delivery

Each phase requires focused implementation tests, a fresh correctness/edge-case review, a UI/UX review where applicable, remediation, and rerun verification before the next phase begins.

## Product and vocabulary contract

- **Project** is the durable local Design object.
- **Screen** is the user-facing name for one stable generated lineage.
- **Revision** is one immutable media artifact within a Screen.
- **Direction set** groups one Explore operation's alternatives.
- **Chosen direction** is a reversible project decision and never deletes alternatives.
- **Prototype** is a bounded, verified graph between exact Screen revisions.
- **Connected preview** remains distinct from an Aiden-owned Screen.
- **Design Language** is a project-local semantic system with exact revision and content hash.

Existing persisted `artboard`, `lineageId`, `mediaId`, and `canonicalOrigin` identifiers remain unchanged. Renderer selection, conversation content, imported prose, and connection state are context rather than authority.

## Phase 1 — Workbench coherence

- Introduce one ephemeral selection projection shared by canvas, transcript artifacts, navigator, composer, History, Comments, and inspector.
- Make **Show on canvas** select, center, activate the requested preview revision, and restore canvas focus.
- Separate historical preview from the durable active revision; add explicit **Make current** and **Refine from this** actions.
- Keep every durable Screen visible as a preview, loading placeholder, or explicit error state; expose Fit recovery when the saved viewport is offscreen.
- Repair the Code rail so long source scrolls internally and cannot displace the inspector.
- Replace ambiguous creation controls with named Explore, Refine, Prototype, Inspect, and Export actions while keeping Select, Hand, Zoom, and Fit as canvas mechanics.
- Separate preview dimensions from surface translation.
- Measure workbench width and switch the context surface from inline rail to contained overlay/full-width sheet without hiding its close control.

Acceptance: every entry point resolves to one exact Screen/revision; reopening never appears to lose durable Screens; History inspection cannot silently mutate `activeMediaId`; narrow layouts pass keyboard, focus, and pointer-interception checks.

## Phase 2 — Project V2 and semantic ownership

- Add dual readers and atomic V1→V2 migration.
- Add durable title policy (`auto-eligible`, `auto-applied`, `manual`) so manual names always win.
- Create blank projects immediately and apply a title only after the first successful Screen publication.
- Add per-Screen surface/frame facts while retaining the project viewport as a preview preference during migration.
- Restrict the generic update endpoint to layout facts; use dedicated main-owned CAS operations for active revision, title, direction, language, prototype, and connection changes.
- Add optional versioned generation provenance before introducing Explore.

Acceptance: old projects preserve every ID, artifact byte, canvas position, timestamp, origin, reference, connection fact, and nonterminal recovery operation; renderer-forged semantic fields fail closed.

## Phase 3 — Explore and Refine

- Persist a strict main-resolved generation intent with the user turn.
- Explore creates 2–4 new lineages from a blank brief or one exact base revision.
- Refine advances exactly one selected lineage from an exact immutable base.
- Persist Direction sets with requested/actual counts, creative range, selected aspects, members, source revision, chosen direction, archive presentation state, and partial/complete status.
- Choose direction never deletes siblings; archive does not recover artifact quota.
- A cancelled user generation with usable candidates asks **Keep draft** or **Discard**. A kept partial Explore remains visibly incomplete and can retry missing members.
- Extend publication ownership and startup reconciliation so artifact and Direction-set stages converge idempotently.

Acceptance: stale bases cannot publish; Explore never advances the selected lineage; partial/cancel/restart cannot create phantom or duplicate members; each new revision exposes exact content-free intent provenance.

## Phase 4 — Project-local Design Language

- Add authored, imported, derived, and workspace-snapshot provenance without weakening the existing workspace freshness contract.
- Store bounded immutable normalized snapshots behind a small owner-only project index.
- Support describe, derive, import, export, apply, compare, reviewed merge, refresh, and detach.
- Bind the exact language revision/hash into generation intent and artifact provenance.
- Define a deterministic Aiden `DESIGN.md` subset. Reject unsafe YAML features, raw HTML, embedded resources, executable directives, paths, credentials, unsupported encodings, control characters, and oversized/deep input.
- Treat all imported human guidance as inert untrusted model context.

Acceptance: prototype-only projects work without Connect App; canonical import/export round trips preserve the normalized hash; malicious fixtures and stale workspace snapshots fail closed.

## Phase 5 — Prototype graph

- Store exact Screen/revision nodes and whitelisted interaction edges separately from HTML.
- Allow bounded click, submit, change, and keyboard triggers with validated destinations and transitions.
- Keep static, unverified, verified, broken/stale, and connected-preview states distinct.
- Verify through the existing network-denied preview host, recording exact source hashes and bounded check evidence.
- Make missing states, keyboard/focus, reduced motion, and link validity explicit checks rather than unverified claims.

Acceptance: no legacy or generated Screen is labeled interactive without verification; stale/remapped revisions visibly invalidate affected edges; the bridge cannot navigate outside its exact project/iframe capability.

## Phase 6 — Export and handoff V2

- Preserve existing single-Screen export.
- Add a versioned deterministic project bundle containing a Project Brief, `DESIGN.md`, exact Screen sources, prototype graph, and bounded references.
- Extend the existing recoverable handoff journal with chosen direction, reviewed Screen subset, Design Language hash, prototype summary, responsive intent, accessibility notes, and exact source hashes.
- Integrate every new project-owned record into duplicate, delete, recovery, and health-check lifecycles.
- Keep export, Connect App, and Continue in Workspace as distinct authority boundaries.

Acceptance: identical reviewed scope produces byte-identical output; stale preview digests require re-preview; no prompt, transcript, credential, absolute path, implicit permission, or rejected source crosses the boundary without explicit review; all V1 journals remain recoverable.

## Cross-cutting verification

- Exact-key parsing, bounds, Unicode, future-version, migration, rollback, and corruption fixtures.
- Store CAS, two-window conflict, crash-boundary, restart reconciliation, duplicate, delete, and feature-disable tests.
- Electron E2E for new project, Show on canvas, History preview, Code rail, offscreen recovery, Explore/cancel, exact-base Refine, Design Language, Prototype, export, relaunch, and preview cleanup.
- Accessibility coverage for roles, names, focus-visible, text-entry focus rules, keyboard-only operation, VoiceOver, reduced motion, high contrast, and forced colors.
- A 20-Screen/10-reference/100-revision fixture with a hard live-iframe cap and truthful offscreen placeholders.
- Full Generative UI, Design recovery, remote exclusion, type-check, lint, build, and exact-head CI gates. Shared chat/remote schema changes trigger iOS and Android contract suites even though the Design canvas remains Mac-only.
- Signed packaged-app acceptance remains separate from source/CI completion.

## Explicit non-goals

- Stitch runtime, code, or asset dependency
- Cloud storage, hosted sharing, or multiplayer
- Public MCP, Figma export, QR/LAN preview, or deployment partners
- Heatmaps, animation generation, or marketing/App Store assets
- Multi-Screen Refine before exact per-output attribution exists
- Five alternatives before measured quota and memory changes
- Automatic repository writes or permission escalation
- Exposing Design Projects or executable artifacts to native clients
70 changes: 24 additions & 46 deletions main/handlers/design-project-params.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,18 @@ import {
parseDesignProjectStartPreviewParams,
} from "./design-project-params.js";

test("Design Project creation separates Prototype storage from Connected App authority", () => {
assert.deepEqual(
parseDesignProjectCreateParams({ title: " Checkout ", connectionState: "prototype-only" }),
{ title: "Checkout", connectionState: "prototype-only" },
);
assert.deepEqual(
parseDesignProjectCreateParams({
title: "Checkout",
connectionState: "connected",
workspaceId: "workspace-1",
}),
{ title: "Checkout", connectionState: "connected", workspaceId: "workspace-1" },
);
assert.throws(
() =>
parseDesignProjectCreateParams({
title: "Forged Prototype",
connectionState: "prototype-only",
workspaceId: "workspace-1",
}),
/workspace connection/u,
);
assert.throws(
() => parseDesignProjectCreateParams({ title: "Missing", connectionState: "connected" }),
/workspace connection/u,
);
assert.throws(
() =>
parseDesignProjectCreateParams({
title: "Legacy authority",
connectionState: "connected",
chatWorkspaceId: "workspace-1",
connectedWorkspaceId: "workspace-1",
}),
/invalid design project request/iu,
);
test("Design Project creation is a main-owned blank local operation", () => {
assert.deepEqual(parseDesignProjectCreateParams({}), {});
for (const request of [
{ title: "Checkout" },
{ connectionState: "connected" },
{ workspaceId: "workspace-1" },
]) {
assert.throws(
() => parseDesignProjectCreateParams(request),
/invalid design project request/iu,
);
}
});

test("connection and preflight IPC accept identities, never paths", () => {
Expand Down Expand Up @@ -77,18 +53,20 @@ test("generic content updates reject forged workspace bindings and transitions",
id: "project:one",
expectedRevision: 2,
canvas: { viewport: "desktop", flowViewport: { x: 0, y: 0, zoom: 1 }, nodes: [] },
referenceAssetIds: [],
};
assert.deepEqual(parseDesignProjectContentUpdateEnvelope(update), update);
assert.throws(
() =>
parseDesignProjectContentUpdateEnvelope({
...update,
connectionState: "connected",
workspaceId: "forged-workspace",
}),
/invalid design project request/iu,
);
for (const claim of [
{ connectionState: "connected" },
{ workspaceId: "forged-workspace" },
{ referenceAssetIds: ["asset:forged"] },
{ designSystemBinding: { id: "design-system:forged", revision: 1 } },
{ previewScriptId: "forged" },
]) {
assert.throws(
() => parseDesignProjectContentUpdateEnvelope({ ...update, ...claim }),
/invalid design project request/iu,
);
}
});

test("source preview IPC is project-bound and rejects renderer workspace claims", () => {
Expand Down
55 changes: 6 additions & 49 deletions main/handlers/design-project-params.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
isDesignProjectOpaqueId,
normalizeDesignProjectTitle,
type DesignProjectConnectionState,
} from "../services/design-project-contract.js";
import { isDesignProjectOpaqueId } from "../services/design-project-contract.js";

function exactRecord(
value: unknown,
Expand Down Expand Up @@ -49,45 +45,17 @@ function boundedString(value: unknown, label: string, max: number): string {
return value;
}

function connectionState(value: unknown): DesignProjectConnectionState {
if (value !== "prototype-only" && value !== "connected") {
throw new Error("Invalid Design Project connection state.");
}
return value;
}

export function parseDesignProjectCreateParams(value: unknown): {
title: string;
connectionState: DesignProjectConnectionState;
workspaceId?: string;
} {
const input = exactRecord(
value,
new Set(["title", "connectionState", "workspaceId"]),
new Set(["title", "connectionState"]),
);
const title = normalizeDesignProjectTitle(input.title);
if (!title) throw new Error("Invalid Design Project title.");
const state = connectionState(input.connectionState);
const workspaceId = input.workspaceId === undefined ? undefined : opaqueId(input.workspaceId);
if (
(state === "prototype-only" && workspaceId !== undefined) ||
(state === "connected" && workspaceId === undefined)
) {
throw new Error("Invalid Design Project workspace connection.");
}
return { title, connectionState: state, ...(workspaceId ? { workspaceId } : {}) };
export function parseDesignProjectCreateParams(value: unknown): Record<string, never> {
exactRecord(value, new Set());
return {};
}

export function parseDesignProjectConnectParams(value: unknown): {
projectId: string;
expectedRevision: number;
workspaceId: string;
} {
const input = exactRecord(
value,
new Set(["projectId", "expectedRevision", "workspaceId"]),
);
const input = exactRecord(value, new Set(["projectId", "expectedRevision", "workspaceId"]));
return {
projectId: opaqueId(input.projectId),
expectedRevision: revision(input.expectedRevision),
Expand All @@ -105,18 +73,7 @@ export function parseDesignProjectPreflightParams(value: unknown): { projectId:
* A workspace binding may only be created by designer:connectProject.
*/
export function parseDesignProjectContentUpdateEnvelope(value: unknown): Record<string, unknown> {
return exactRecord(
value,
new Set([
"id",
"expectedRevision",
"canvas",
"referenceAssetIds",
"designSystemBinding",
"previewScriptId",
]),
new Set(["id", "expectedRevision", "canvas", "referenceAssetIds"]),
);
return exactRecord(value, new Set(["id", "expectedRevision", "canvas"]));
}

export function parseDesignProjectPreviewParams(value: unknown): { projectId: string } {
Expand Down
Loading