diff --git a/.agents/backlog-policy.md b/.agents/backlog-policy.md new file mode 100644 index 00000000..3ff7f6f0 --- /dev/null +++ b/.agents/backlog-policy.md @@ -0,0 +1,19 @@ +# Backlog Policy + +GitHub Project: https://github.com/users/espetro/projects/6 + +## Refined task fields + +- **Milestone** — maps to iteration/quarter (e.g. `2026 Q3`). Create the milestone in the repo if it doesn't exist yet. +- **Size** — effort estimate: `XS`/`S`/`M`/`L`/`XL`. Include testing + bug potential per contact surface. +- **Start date** / **Target date** — scheduled window for the task. +- **Label** — classification, drives client positioning: `feature`, `bug`, `cosmetic`, `infra`. Create the label in the repo if it doesn't exist yet. + +## Workflow + +1. Create a GitHub issue in `espetro/calca` with title, body, classification label, milestone. +2. Add it to project 6: `gh project item-add 6 --owner espetro --url `. +3. Set Size/Start date/Target date via `gh project item-edit --project-id --id --field-id ...`. +4. Reference the issue number in the PR description and in the corresponding `.agents/plans/` doc. + +No orphan work: every plan/implementation must link back to a refined task here. diff --git a/apps/web/src/shared/types/canvas.ts b/apps/web/src/shared/types/canvas.ts index d9088e1c..e0c5fcb6 100644 --- a/apps/web/src/shared/types/canvas.ts +++ b/apps/web/src/shared/types/canvas.ts @@ -1,6 +1 @@ -export interface Point { - x: number; - y: number; -} - -export type ToolMode = "select" | "draw-area" | "edit-component" | "comment"; +export type { Point, ToolMode } from "@app/shared"; diff --git a/apps/web/src/shared/types/comment.ts b/apps/web/src/shared/types/comment.ts index 2ba54697..aecdaa2b 100644 --- a/apps/web/src/shared/types/comment.ts +++ b/apps/web/src/shared/types/comment.ts @@ -1,20 +1 @@ -export type CommentStatus = "waiting" | "working" | "done"; - -export interface CommentMessage { - id: string; - role: "user" | "calca"; - text: string; - createdAt: number; -} - -export interface Comment { - id: string; - position: import("./canvas").Point; // Relative to the iteration - text: string; - number: number; - resolved?: boolean; - createdAt: number; - status?: CommentStatus; - aiResponse?: string; - thread?: CommentMessage[]; -} +export type { Comment, CommentMessage, CommentStatus } from "@app/shared"; diff --git a/apps/web/src/shared/types/design.ts b/apps/web/src/shared/types/design.ts index 0bbb89fa..20860781 100644 --- a/apps/web/src/shared/types/design.ts +++ b/apps/web/src/shared/types/design.ts @@ -1,36 +1 @@ -export interface DesignIteration { - id: string; - html: string; - label: string; - position: import("./canvas").Point; - width: number; - height: number; - prompt: string; - comments: import("./comment").Comment[]; - isLoading?: boolean; - isRegenerating?: boolean; -} - -export interface SummaryData { - title: string; - rationale: string; -} - -export interface GenerationGroup { - id: string; - prompt: string; - iterations: DesignIteration[]; - position: import("./canvas").Point; - createdAt: number; - summary?: SummaryData; -} - -export interface CanvasImage { - id: string; - dataUrl: string; // Base64 data URI - name: string; - width: number; - height: number; - position: import("./canvas").Point; - thumbnail: string; // Smaller version for UI -} +export type { CanvasImage, DesignIteration, GenerationGroup, SummaryData } from "@app/shared"; diff --git a/apps/web/src/shared/types/pipeline.ts b/apps/web/src/shared/types/pipeline.ts index 7fa5f11a..2b9936f6 100644 --- a/apps/web/src/shared/types/pipeline.ts +++ b/apps/web/src/shared/types/pipeline.ts @@ -1,32 +1,2 @@ -/** Pipeline stages for multi-model generation */ - -export type PipelineStage = - | "queued" - | "layout" - | "images" - | "compositing" - | "review" - | "refining" - | "done" - | "error"; - -export interface PipelineStatus { - stage: PipelineStage; - progress: number; // 0-1 - skipped?: boolean; - reason?: string; -} - -export const STAGE_CONFIG: Record< - PipelineStage, - { label: string; progress: number; icon: string } -> = { - compositing: { label: "Compositing…", progress: 0.65, icon: "🔧" }, - done: { label: "Complete", progress: 1, icon: "✅" }, - error: { label: "Error", progress: 0, icon: "❌" }, - images: { label: "Adding images…", progress: 0.45, icon: "🎨" }, - layout: { label: "Generating layout…", progress: 0.2, icon: "🏗️" }, - queued: { label: "Queued", progress: 0, icon: "⏳" }, - refining: { label: "Preparing next variation…", progress: 0.9, icon: "✨" }, - review: { label: "Reviewing design…", progress: 0.8, icon: "👁️" }, -}; +export type { PipelineStage, PipelineStatus } from "@app/shared"; +export { STAGE_CONFIG } from "@app/shared"; diff --git a/bun.lock b/bun.lock index 072f4e8f..162937d3 100644 --- a/bun.lock +++ b/bun.lock @@ -164,7 +164,7 @@ "@ai-sdk/google": "^3.0.61", "@ai-sdk/openai-compatible": "^2.0.41", "@ai-sdk/provider": "3.0.8", - "@ai-sdk/provider-utils": "4.0.23", + "@ai-sdk/provider-utils": "^4.0.26", "@app/logger": "workspace:*", "ai": "^6.0.156", "zod": "^4.3.6", @@ -2713,8 +2713,6 @@ "@ai-sdk/ui-utils-v5/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@app/core/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], - "@app/core/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], "@app/shared/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], diff --git a/packages/core/package.json b/packages/core/package.json index 4411a5c7..ab3fbad7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -34,7 +34,7 @@ "@ai-sdk/google": "^3.0.61", "@ai-sdk/openai-compatible": "^2.0.41", "@ai-sdk/provider": "3.0.8", - "@ai-sdk/provider-utils": "4.0.23", + "@ai-sdk/provider-utils": "^4.0.26", "@app/logger": "workspace:*", "ai": "^6.0.156", "zod": "^4.3.6" diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 7739ff9f..f964d5f7 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -25,3 +25,8 @@ export { validateSummary, type SummaryOutput, } from "./schemas/summary"; + +export * from "./types/canvas"; +export * from "./types/design"; +export * from "./types/comment"; +export * from "./types/pipeline"; diff --git a/packages/shared/src/types/canvas.ts b/packages/shared/src/types/canvas.ts new file mode 100644 index 00000000..d9088e1c --- /dev/null +++ b/packages/shared/src/types/canvas.ts @@ -0,0 +1,6 @@ +export interface Point { + x: number; + y: number; +} + +export type ToolMode = "select" | "draw-area" | "edit-component" | "comment"; diff --git a/packages/shared/src/types/comment.ts b/packages/shared/src/types/comment.ts new file mode 100644 index 00000000..0bfa78c1 --- /dev/null +++ b/packages/shared/src/types/comment.ts @@ -0,0 +1,22 @@ +import type { Point } from "./canvas"; + +export type CommentStatus = "waiting" | "working" | "done"; + +export interface CommentMessage { + id: string; + role: "user" | "calca"; + text: string; + createdAt: number; +} + +export interface Comment { + id: string; + position: Point; // Relative to the iteration + text: string; + number: number; + resolved?: boolean; + createdAt: number; + status?: CommentStatus; + aiResponse?: string; + thread?: CommentMessage[]; +} diff --git a/packages/shared/src/types/design.ts b/packages/shared/src/types/design.ts new file mode 100644 index 00000000..289c596b --- /dev/null +++ b/packages/shared/src/types/design.ts @@ -0,0 +1,39 @@ +import type { Point } from "./canvas"; +import type { Comment } from "./comment"; + +export interface DesignIteration { + id: string; + html: string; + label: string; + position: Point; + width: number; + height: number; + prompt: string; + comments: Comment[]; + isLoading?: boolean; + isRegenerating?: boolean; +} + +export interface SummaryData { + title: string; + rationale: string; +} + +export interface GenerationGroup { + id: string; + prompt: string; + iterations: DesignIteration[]; + position: Point; + createdAt: number; + summary?: SummaryData; +} + +export interface CanvasImage { + id: string; + dataUrl: string; // Base64 data URI + name: string; + width: number; + height: number; + position: Point; + thumbnail: string; // Smaller version for UI +} diff --git a/packages/shared/src/types/pipeline.ts b/packages/shared/src/types/pipeline.ts new file mode 100644 index 00000000..7fa5f11a --- /dev/null +++ b/packages/shared/src/types/pipeline.ts @@ -0,0 +1,32 @@ +/** Pipeline stages for multi-model generation */ + +export type PipelineStage = + | "queued" + | "layout" + | "images" + | "compositing" + | "review" + | "refining" + | "done" + | "error"; + +export interface PipelineStatus { + stage: PipelineStage; + progress: number; // 0-1 + skipped?: boolean; + reason?: string; +} + +export const STAGE_CONFIG: Record< + PipelineStage, + { label: string; progress: number; icon: string } +> = { + compositing: { label: "Compositing…", progress: 0.65, icon: "🔧" }, + done: { label: "Complete", progress: 1, icon: "✅" }, + error: { label: "Error", progress: 0, icon: "❌" }, + images: { label: "Adding images…", progress: 0.45, icon: "🎨" }, + layout: { label: "Generating layout…", progress: 0.2, icon: "🏗️" }, + queued: { label: "Queued", progress: 0, icon: "⏳" }, + refining: { label: "Preparing next variation…", progress: 0.9, icon: "✨" }, + review: { label: "Reviewing design…", progress: 0.8, icon: "👁️" }, +};