mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Add planning mode for issue work (#5353)
## Thinking Path > - Paperclip is a control plane for autonomous AI companies. > - Issues are the core unit of work, and issue comments are how board users and agents coordinate execution. > - Some issue conversations need to produce plans and approvals instead of immediate implementation work. > - The existing issue contract did not distinguish standard execution comments from planning-oriented issue work. > - This pull request adds an issue work-mode contract and board UI affordances for standard vs planning mode. > - The benefit is that planning-mode issues can be created, displayed, discussed, and carried through agent heartbeat context without losing the normal issue workflow. ## What Changed - Added `standard` / `planning` issue work-mode contracts across DB, shared validators/types, server issue flows, plugin protocol, and adapter heartbeat payloads. - Added an idempotent `0081_optimal_dormammu` migration for `issues.work_mode`, ordered after current `public-gh/master` migrations. - Updated heartbeat/context summaries and issue-thread interaction behavior so planning work mode is preserved when creating suggested follow-up issues. - Added UI support for planning-mode issue creation, issue rows, detail composer styling, and composer work-mode toggles. - Added focused server/shared/UI tests plus a Playwright visual verification spec for planning-mode surfaces. - Rebased the branch onto current `public-gh/master` and added durable planning-mode screenshots under `doc/assets/pap-3368/`. ## Verification - `pnpm --filter @paperclipai/db run check:migrations` - `pnpm exec vitest run --project @paperclipai/shared packages/shared/src/validators/issue.test.ts` - `pnpm exec vitest run --project @paperclipai/server server/src/__tests__/heartbeat-context-summary.test.ts server/src/__tests__/issue-thread-interactions-service.test.ts server/src/__tests__/issues-goal-context-routes.test.ts --pool=forks --poolOptions.forks.isolate=true` - `pnpm exec vitest run --project @paperclipai/ui ui/src/components/IssueChatThread.test.tsx ui/src/components/NewIssueDialog.test.tsx ui/src/components/IssueRow.test.tsx ui/src/pages/IssueDetail.test.tsx` - `pnpm exec vitest run --project @paperclipai/adapter-utils packages/adapter-utils/src/server-utils.test.ts` - `PAPERCLIP_E2E_SKIP_LLM=true npx playwright test --config tests/e2e/playwright.config.ts tests/e2e/planning-mode-visual-verification.spec.ts` ## Screenshots Desktop planning detail:  Desktop planning row:  Desktop staged standard toggle:  Mobile planning detail:  Mobile planning row:  ## Risks - Medium migration risk: this adds a non-null issue column. The migration uses `ADD COLUMN IF NOT EXISTS` so installations that applied an older branch-local migration number can still apply the final numbered migration safely. - Medium contract risk: issue payloads, plugin payloads, and adapter heartbeat payloads now include work mode; compatibility is handled by defaulting missing values to `standard`. - UI risk is moderate because composer controls changed; focused component tests and visual e2e coverage exercise standard vs planning display and toggle behavior. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, GPT-5 coding agent in a local Paperclip worktree, with shell/tool use. Exact context-window size is not exposed in this runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
320fd5d23b
commit
a1b30c9f35
65 changed files with 1539 additions and 214 deletions
|
|
@ -146,6 +146,8 @@ export const INBOX_MINE_ISSUE_STATUS_FILTER = INBOX_MINE_ISSUE_STATUSES.join(","
|
|||
|
||||
export const ISSUE_PRIORITIES = ["critical", "high", "medium", "low"] as const;
|
||||
export type IssuePriority = (typeof ISSUE_PRIORITIES)[number];
|
||||
export const ISSUE_WORK_MODES = ["standard", "planning"] as const;
|
||||
export type IssueWorkMode = (typeof ISSUE_WORK_MODES)[number];
|
||||
export const MAX_ISSUE_REQUEST_DEPTH = 1024;
|
||||
|
||||
export const ISSUE_COMMENT_AUTHOR_TYPES = ["user", "agent", "system"] as const;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export {
|
|||
INBOX_MINE_ISSUE_STATUSES,
|
||||
INBOX_MINE_ISSUE_STATUS_FILTER,
|
||||
ISSUE_PRIORITIES,
|
||||
ISSUE_WORK_MODES,
|
||||
MAX_ISSUE_REQUEST_DEPTH,
|
||||
ISSUE_COMMENT_AUTHOR_TYPES,
|
||||
ISSUE_COMMENT_METADATA_ROW_TYPES,
|
||||
|
|
@ -134,6 +135,7 @@ export {
|
|||
type AgentIconName,
|
||||
type IssueStatus,
|
||||
type IssuePriority,
|
||||
type IssueWorkMode,
|
||||
type IssueCommentAuthorType,
|
||||
type IssueCommentMetadataRowType,
|
||||
type IssueCommentPresentationKind,
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ export type {
|
|||
} from "./work-product.js";
|
||||
export type {
|
||||
Issue,
|
||||
IssueWorkMode,
|
||||
IssueAssigneeAdapterOverrides,
|
||||
IssueBlockerAttention,
|
||||
IssueBlockerAttentionReason,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import type {
|
|||
IssueExecutionStateStatus,
|
||||
IssueOriginKind,
|
||||
IssuePriority,
|
||||
IssueWorkMode,
|
||||
ModelProfileKey,
|
||||
IssueThreadInteractionContinuationPolicy,
|
||||
IssueThreadInteractionKind,
|
||||
|
|
@ -26,6 +27,8 @@ import type { Project, ProjectWorkspace } from "./project.js";
|
|||
import type { ExecutionWorkspace, IssueExecutionWorkspaceSettings } from "./workspace-runtime.js";
|
||||
import type { IssueWorkProduct } from "./work-product.js";
|
||||
|
||||
export type { IssueWorkMode };
|
||||
|
||||
export interface IssueAncestorProject {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -302,6 +305,7 @@ export interface Issue {
|
|||
title: string;
|
||||
description: string | null;
|
||||
status: IssueStatus;
|
||||
workMode: IssueWorkMode;
|
||||
priority: IssuePriority;
|
||||
assigneeAgentId: string | null;
|
||||
assigneeUserId: string | null;
|
||||
|
|
@ -454,6 +458,7 @@ export interface SuggestedTaskDraft {
|
|||
title: string;
|
||||
description?: string | null;
|
||||
priority?: IssuePriority | null;
|
||||
workMode?: IssueWorkMode | null;
|
||||
assigneeAgentId?: string | null;
|
||||
assigneeUserId?: string | null;
|
||||
projectId?: string | null;
|
||||
|
|
|
|||
|
|
@ -127,6 +127,26 @@ describe("issue validators", () => {
|
|||
expect(parsed.requestDepth).toBe(MAX_ISSUE_REQUEST_DEPTH);
|
||||
});
|
||||
|
||||
it("defaults issue work mode to standard and accepts planning", () => {
|
||||
expect(createIssueSchema.parse({ title: "Plan first" }).workMode).toBe("standard");
|
||||
expect(createIssueSchema.parse({ title: "Plan first", workMode: "planning" }).workMode).toBe("planning");
|
||||
expect(updateIssueSchema.parse({ workMode: "planning" }).workMode).toBe("planning");
|
||||
expect(suggestedTaskDraftSchema.parse({
|
||||
clientKey: "planning-child",
|
||||
title: "Plan child",
|
||||
workMode: "planning",
|
||||
}).workMode).toBe("planning");
|
||||
});
|
||||
|
||||
it("rejects unknown issue work modes", () => {
|
||||
expect(createIssueSchema.safeParse({ title: "Plan first", workMode: "normal" }).success).toBe(false);
|
||||
expect(suggestedTaskDraftSchema.safeParse({
|
||||
clientKey: "bad-child",
|
||||
title: "Bad child",
|
||||
workMode: "analysis",
|
||||
}).success).toBe(false);
|
||||
});
|
||||
|
||||
it("clamps oversized requestDepth values on update", () => {
|
||||
const parsed = updateIssueSchema.parse({
|
||||
requestDepth: MAX_ISSUE_REQUEST_DEPTH + 1,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
ISSUE_COMMENT_PRESENTATION_TONES,
|
||||
ISSUE_MONITOR_SCHEDULED_BY,
|
||||
ISSUE_PRIORITIES,
|
||||
ISSUE_WORK_MODES,
|
||||
clampIssueRequestDepth,
|
||||
ISSUE_STATUSES,
|
||||
ISSUE_THREAD_INTERACTION_CONTINUATION_POLICIES,
|
||||
|
|
@ -182,6 +183,7 @@ export const createIssueSchema = z.object({
|
|||
title: z.string().min(1),
|
||||
description: multilineTextSchema.optional().nullable(),
|
||||
status: z.enum(ISSUE_STATUSES).optional().default("backlog"),
|
||||
workMode: z.enum(ISSUE_WORK_MODES).optional().default("standard"),
|
||||
priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"),
|
||||
assigneeAgentId: z.string().uuid().optional().nullable(),
|
||||
assigneeUserId: z.string().optional().nullable(),
|
||||
|
|
@ -353,6 +355,7 @@ export const suggestedTaskDraftSchema = z.object({
|
|||
title: z.string().trim().min(1).max(240),
|
||||
description: multilineTextSchema.pipe(z.string().trim().max(20000)).nullable().optional(),
|
||||
priority: z.enum(ISSUE_PRIORITIES).nullable().optional(),
|
||||
workMode: z.enum(ISSUE_WORK_MODES).nullable().optional(),
|
||||
assigneeAgentId: z.string().uuid().nullable().optional(),
|
||||
assigneeUserId: z.string().trim().min(1).nullable().optional(),
|
||||
projectId: z.string().uuid().nullable().optional(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue