Present ordered sub-issues as a workflow checklist (#4523)
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Operators use issue detail pages and child issue lists to understand
multi-step execution plans.
> - Ordered sub-issues currently read like a flat table, so dependency
chains and current next steps are harder to scan.
> - The branch work adds a workflow-oriented presentation for child
issues without changing the single-assignee task model.
> - This pull request makes ordered sub-issues read more like a progress
checklist while preserving normal issue list controls.
> - The benefit is that operators can see completed steps, active work,
blocked follow-ups, and dependency order at a glance.
## What Changed
- Added workflow sorting utilities and tests for dependency-aware child
issue ordering.
- Added sub-issue progress summary, checklist numbering, current-step
affordances, blocker context, and done-state de-emphasis in the issue
list UI.
- Wired issue detail sub-issue panels to use the workflow sort/progress
checklist presentation.
- Updated issue service behavior/tests for child issue ordering inputs
used by the UI.
- Added a Storybook visual review fixture and screenshot helper for the
sub-issue workflow checklist surface.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/issues-service.test.ts
ui/src/components/IssueRow.test.tsx
ui/src/components/IssuesList.test.tsx ui/src/pages/IssueDetail.test.tsx
ui/src/lib/issue-detail-subissues.test.ts
ui/src/lib/workflow-sort.test.ts`
- Result: 6 test files passed, 55 tests passed, 34 embedded Postgres
issue-service tests skipped because `@embedded-postgres/darwin-x64` is
unavailable on this host.
- Visual review: generated Storybook screenshots from the existing local
Storybook server on port 6006 with `node
scripts/screenshot-subissues.mjs /tmp/pap-2189-subissues-screens
http://localhost:6006`.
- Screenshot artifacts:
- Desktop dark: 
- Desktop light: 
- Mobile dark: 
- Mobile light: 
- Local Storybook note: starting a second Storybook process selected
port 6008 because 6006 was occupied, then Vite failed with an esbuild
host/binary version mismatch (`0.25.12` host vs `0.27.3` binary). The
already-running Storybook server on 6006 served the fixture successfully
for screenshots.
## Risks
- Medium UI risk: the issue list now has additional sub-issue-specific
visual states, so dense lists should be checked for spacing and
scanability.
- Low ordering risk: workflow sorting is covered by focused unit tests,
but unusual dependency topologies may still need reviewer attention.
- No migration risk: this PR does not add database migrations or touch
`pnpm-lock.yaml`.
> 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, tool-enabled shell/git/GitHub
workflow. Context window is runtime-provided and not exposed in this
environment.
## 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>
2026-04-26 07:36:49 -05:00
import type { Meta , StoryObj } from "@storybook/react-vite" ;
import { useState } from "react" ;
import type { Issue } from "@paperclipai/shared" ;
import { useQueryClient } from "@tanstack/react-query" ;
import { IssuesList } from "@/components/IssuesList" ;
import { queryKeys } from "@/lib/queryKeys" ;
import {
createIssue ,
storybookAgents ,
storybookAuthSession ,
storybookCompanies ,
storybookIssueLabels ,
storybookProjects ,
} from "../fixtures/paperclipData" ;
const companyId = "company-storybook" ;
const parentId = "issue-pap-1953" ;
type BlockerRef = NonNullable < Issue [ "blockedBy" ] > [ number ] ;
function child ( overrides : Partial < Issue > ) : Issue {
return createIssue ( {
parentId ,
projectId : storybookProjects [ 0 ] ! . id ,
projectWorkspaceId : storybookProjects [ 0 ] ! . workspaces [ 0 ] ? . id ? ? null ,
goalId : null ,
blockedBy : [ ] ,
blocks : [ ] ,
labelIds : [ ] ,
labels : [ ] ,
. . . overrides ,
} ) ;
}
const blockerRef = ( issue : Issue ) : BlockerRef = > ( {
id : issue.id ,
identifier : issue.identifier ,
title : issue.title ,
status : issue.status ,
priority : issue.priority ,
assigneeAgentId : issue.assigneeAgentId ,
assigneeUserId : issue.assigneeUserId ,
} ) ;
const baseCreatedAt = new Date ( "2026-04-10T12:00:00.000Z" ) . getTime ( ) ;
const createdAt = ( offsetMinutes : number ) = >
new Date ( baseCreatedAt + offsetMinutes * 60 _000 ) ;
// Mirrors the PAP-1953 topology called out in the PAP-2189 plan:
// 1954 Scoping (done) — root
// 1955 Security scoping (done) — root
// 1960 Phase 1 (done) → 1961 Phase 2 (done)
// 1962 Phase 3 (done) → 1963 Phase 4 (done)
// → 1964 Phase 5 (in_progress)
// → 1965 Phase 6 (blocked)
// → 1966 Phase 7 (blocked)
const scoping = child ( {
id : "issue-pap-1954" ,
identifier : "PAP-1954" ,
issueNumber : 1954 ,
title : "Scoping review" ,
status : "done" ,
priority : "medium" ,
completedAt : createdAt ( 120 ) ,
createdAt : createdAt ( 0 ) ,
} ) ;
const security = child ( {
id : "issue-pap-1955" ,
identifier : "PAP-1955" ,
issueNumber : 1955 ,
title : "Security scoping" ,
status : "done" ,
priority : "medium" ,
completedAt : createdAt ( 180 ) ,
createdAt : createdAt ( 10 ) ,
} ) ;
const phase1 = child ( {
id : "issue-pap-1960" ,
identifier : "PAP-1960" ,
issueNumber : 1960 ,
title : "Phase 1 — groundwork" ,
status : "done" ,
priority : "medium" ,
completedAt : createdAt ( 600 ) ,
createdAt : createdAt ( 20 ) ,
} ) ;
const phase2 = child ( {
id : "issue-pap-1961" ,
identifier : "PAP-1961" ,
issueNumber : 1961 ,
title : "Phase 2 — integration" ,
status : "done" ,
priority : "medium" ,
completedAt : createdAt ( 720 ) ,
createdAt : createdAt ( 30 ) ,
blockedBy : [ blockerRef ( phase1 ) ] ,
} ) ;
const phase3 = child ( {
id : "issue-pap-1962" ,
identifier : "PAP-1962" ,
issueNumber : 1962 ,
title : "Phase 3 — data model" ,
status : "done" ,
priority : "medium" ,
completedAt : createdAt ( 800 ) ,
createdAt : createdAt ( 40 ) ,
} ) ;
const phase4 = child ( {
id : "issue-pap-1963" ,
identifier : "PAP-1963" ,
issueNumber : 1963 ,
title : "Phase 4 — API surface" ,
status : "done" ,
priority : "medium" ,
completedAt : createdAt ( 900 ) ,
createdAt : createdAt ( 50 ) ,
blockedBy : [ blockerRef ( phase3 ) ] ,
} ) ;
const phase5 = child ( {
id : "issue-pap-1964" ,
identifier : "PAP-1964" ,
issueNumber : 1964 ,
title : "Phase 5 — UI polish" ,
status : "in_progress" ,
priority : "high" ,
createdAt : createdAt ( 60 ) ,
blockedBy : [ blockerRef ( phase4 ) ] ,
} ) ;
const phase6 = child ( {
id : "issue-pap-1965" ,
identifier : "PAP-1965" ,
issueNumber : 1965 ,
title : "Phase 6 — telemetry wiring" ,
status : "blocked" ,
priority : "medium" ,
createdAt : createdAt ( 70 ) ,
blockedBy : [ blockerRef ( phase5 ) ] ,
} ) ;
const phase7 = child ( {
id : "issue-pap-1966" ,
identifier : "PAP-1966" ,
issueNumber : 1966 ,
title : "Phase 7 — rollout" ,
status : "blocked" ,
priority : "medium" ,
createdAt : createdAt ( 80 ) ,
blockedBy : [ blockerRef ( phase6 ) ] ,
} ) ;
const subIssues : Issue [ ] = [
scoping ,
security ,
phase1 ,
phase2 ,
phase3 ,
phase4 ,
phase5 ,
phase6 ,
phase7 ,
] ;
const viewStateKey = "storybook:sub-issues-workflow:list" ;
const scopedKey = ` ${ viewStateKey } : ${ companyId } ` ;
function hydrateQueries ( client : ReturnType < typeof useQueryClient > ) {
Add cheap model profiles for local adapters (#4881)
## Thinking Path
> - Paperclip is a control plane for autonomous AI companies, where
adapters are the boundary between the board, agents, and execution
runtimes.
> - Local adapters currently expose a primary runtime configuration, but
operators often need a cheaper model lane for routine or low-risk work.
> - That cheap lane has to stay adapter-owned: runtime profile settings
should not mutate the primary adapter config or bypass existing
auth/secret mediation.
> - Issue creation also needs an ergonomic way to request primary,
cheap, or custom model behavior for a selected assignee.
> - This pull request adds a first-class `cheap` model profile contract
across adapter capabilities, heartbeat config resolution, agent
configuration, and issue creation.
> - The benefit is cheaper task execution can be configured and
requested explicitly while preserving adapter boundaries, secret
handling, and audit visibility.
## What Changed
- Added adapter model-profile capability metadata and a `cheap` profile
contract for supported local adapters.
- Applied `runtimeConfig.modelProfiles.cheap.adapterConfig` during
heartbeat config resolution, including requested/applied/fallback run
metadata.
- Added agent configuration UI for cheap model profile settings without
writing those settings into primary `adapterConfig`.
- Added New Issue assignee model lane controls for Primary / Cheap /
Custom and request payload handling.
- Added run ledger profile badges and Storybook stories for the new
cheap-lane UI states.
- Added tests for validators, heartbeat model profile application,
permission/secret mediation, UI payload helpers, and run ledger
rendering.
- Added committed UI verification screenshots under
`docs/pr-screenshots/pap-2837/`.
- Addressed Greptile review feedback around cheap-profile defaults,
shared profile types, and fallback test data.
## Verification
Local:
- `pnpm exec vitest run packages/shared/src/validators/issue.test.ts
server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
server/src/__tests__/heartbeat-model-profile.test.ts
ui/src/components/IssueRunLedger.test.tsx
ui/src/lib/agent-config-patch.test.ts
ui/src/lib/issue-assignee-overrides.test.ts
ui/src/lib/new-agent-runtime-config.test.ts` — passed, 8 files / 103
tests.
- `pnpm exec vitest run ui/src/lib/new-agent-runtime-config.test.ts
ui/src/components/IssueRunLedger.test.tsx` — passed after
Greptile/rebase follow-up, 2 files / 17 tests.
- `pnpm --filter @paperclipai/ui typecheck` — passed after
Greptile/rebase follow-up.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed.
- `pnpm test:run` — did not complete successfully in this local
worktree: it stopped in pre-existing `@paperclipai/adapter-utils`
sandbox/SSH fixture suites outside this PR diff. Failures were 5s local
timeouts plus `git init -b` unsupported by this machine's Git 2.21.0.
The branch-specific targeted suites above passed.
- Branch was fetched/rebased onto `public-gh/master`; `git rev-list
--left-right --count public-gh/master...HEAD` reports `0 9`.
Remote PR checks on latest head
`e30bf399146451c86cee98ed528d51d33fa5af5a`:
- `policy` — passed.
- `verify` — passed.
- `e2e` — passed.
- `Greptile Review` — passed, confidence score 5/5; Greptile review
threads resolved.
- `security/snyk (cryppadotta)` — passed.
Screenshots:
- [New issue cheap lane
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/newissue-cheap-desktop.png)
- [New issue custom lane
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/newissue-custom-desktop.png)
- [New issue unsupported adapter
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/newissue-unsupported-desktop.png)
- [Run ledger model profile badges
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/runledger-profile-badges-desktop.png)
- Mobile variants are also in `docs/pr-screenshots/pap-2837/`.
## Risks
- Medium: heartbeat config mediation now merges runtime model profiles
into adapter configs, so adapter secret normalization and host-command
restrictions must keep covering nested config paths.
- Medium: the UI adds another issue creation choice; unsupported
adapters must keep hiding the cheap lane and preserve primary behavior.
- Low migration risk: no database migration is included.
> 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 coding agent using GPT-5-class reasoning with repo tool use
and command execution. Exact served model/context window was not exposed
by the 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
- [ ] 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>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 15:32:04 -05:00
client . setQueryData ( queryKeys . companies . all , { companies : storybookCompanies , unauthorized : false } ) ;
Present ordered sub-issues as a workflow checklist (#4523)
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Operators use issue detail pages and child issue lists to understand
multi-step execution plans.
> - Ordered sub-issues currently read like a flat table, so dependency
chains and current next steps are harder to scan.
> - The branch work adds a workflow-oriented presentation for child
issues without changing the single-assignee task model.
> - This pull request makes ordered sub-issues read more like a progress
checklist while preserving normal issue list controls.
> - The benefit is that operators can see completed steps, active work,
blocked follow-ups, and dependency order at a glance.
## What Changed
- Added workflow sorting utilities and tests for dependency-aware child
issue ordering.
- Added sub-issue progress summary, checklist numbering, current-step
affordances, blocker context, and done-state de-emphasis in the issue
list UI.
- Wired issue detail sub-issue panels to use the workflow sort/progress
checklist presentation.
- Updated issue service behavior/tests for child issue ordering inputs
used by the UI.
- Added a Storybook visual review fixture and screenshot helper for the
sub-issue workflow checklist surface.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/issues-service.test.ts
ui/src/components/IssueRow.test.tsx
ui/src/components/IssuesList.test.tsx ui/src/pages/IssueDetail.test.tsx
ui/src/lib/issue-detail-subissues.test.ts
ui/src/lib/workflow-sort.test.ts`
- Result: 6 test files passed, 55 tests passed, 34 embedded Postgres
issue-service tests skipped because `@embedded-postgres/darwin-x64` is
unavailable on this host.
- Visual review: generated Storybook screenshots from the existing local
Storybook server on port 6006 with `node
scripts/screenshot-subissues.mjs /tmp/pap-2189-subissues-screens
http://localhost:6006`.
- Screenshot artifacts:
- Desktop dark: 
- Desktop light: 
- Mobile dark: 
- Mobile light: 
- Local Storybook note: starting a second Storybook process selected
port 6008 because 6006 was occupied, then Vite failed with an esbuild
host/binary version mismatch (`0.25.12` host vs `0.27.3` binary). The
already-running Storybook server on 6006 served the fixture successfully
for screenshots.
## Risks
- Medium UI risk: the issue list now has additional sub-issue-specific
visual states, so dense lists should be checked for spacing and
scanability.
- Low ordering risk: workflow sorting is covered by focused unit tests,
but unusual dependency topologies may still need reviewer attention.
- No migration risk: this PR does not add database migrations or touch
`pnpm-lock.yaml`.
> 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, tool-enabled shell/git/GitHub
workflow. Context window is runtime-provided and not exposed in this
environment.
## 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>
2026-04-26 07:36:49 -05:00
client . setQueryData ( queryKeys . auth . session , storybookAuthSession ) ;
client . setQueryData ( queryKeys . agents . list ( companyId ) , storybookAgents ) ;
client . setQueryData ( queryKeys . projects . list ( companyId ) , storybookProjects ) ;
client . setQueryData ( queryKeys . issues . labels ( companyId ) , storybookIssueLabels ) ;
client . setQueryData ( queryKeys . issues . list ( companyId ) , subIssues ) ;
client . setQueryData ( queryKeys . access . companyUserDirectory ( companyId ) , {
users : [
{
principalId : "user-board" ,
status : "active" ,
user : {
id : "user-board" ,
email : "riley@paperclip.local" ,
name : "Riley Board" ,
image : null ,
} ,
} ,
] ,
} ) ;
client . setQueryData ( queryKeys . instance . experimentalSettings , {
enableIsolatedWorkspaces : true ,
enableRoutineTriggers : true ,
} ) ;
}
function Hydrated ( { children } : { children : React.ReactNode } ) {
const queryClient = useQueryClient ( ) ;
const [ ready ] = useState ( ( ) = > {
hydrateQueries ( queryClient ) ;
if ( typeof window !== "undefined" ) {
window . localStorage . removeItem ( scopedKey ) ;
window . localStorage . removeItem ( ` ${ scopedKey } :issue-columns ` ) ;
}
return true ;
} ) ;
return ready ? children : null ;
}
function SubIssuesWorkflowPanel() {
return (
< div className = "paperclip-story" >
< main className = "paperclip-story__inner" >
< div className = "mx-auto max-w-5xl space-y-5" >
< header className = "space-y-1" >
< div className = "paperclip-story__label" > Issue Detail · Sub - issues < / div >
< h1 className = "text-2xl font-semibold tracking-tight" >
Workflow - sorted sub - issues with checklist affordances
< / h1 >
< p className = "max-w-3xl text-sm text-muted-foreground" >
Fixture mirrors the PAP - 1953 topology called out in the PAP - 2189
plan : two standalone scoping items , a Phase 1 → 2 pair , and a long
Phase 3 → 4 → 5 → 6 → 7 chain . The panel renders with
< code className = "mx-1 rounded bg-muted px-1 py-0.5 font-mono text-xs" >
defaultSortField = "workflow"
< / code >
and
< code className = "mx-1 rounded bg-muted px-1 py-0.5 font-mono text-xs" >
showProgressSummary
< / code >
so reviewers see the full checklist surface in isolation .
< / p >
< / header >
< div className = "rounded-lg border border-border bg-background p-5" >
< IssuesList
issues = { subIssues }
agents = { storybookAgents }
projects = { storybookProjects }
viewStateKey = { viewStateKey }
defaultSortField = "workflow"
showProgressSummary
onUpdateIssue = { ( ) = > undefined }
createIssueLabel = "Sub-issue"
/ >
< / div >
< / div >
< / main >
< / div >
) ;
}
const meta = {
title : "UX Labs/Sub-issues Workflow Checklist" ,
component : SubIssuesWorkflowPanel ,
parameters : {
layout : "fullscreen" ,
docs : {
description : {
component :
"Review surface for the PAP-2189 checklist-style sub-issues work. Renders the IssuesList component with the Sub-issues panel props so the progress strip, workflow sort, step gutter, current marker, done de-emphasis, and blocker chips are all visible against a PAP-1953-like topology." ,
} ,
} ,
} ,
decorators : [
( StoryRender ) = > (
< Hydrated >
< StoryRender / >
< / Hydrated >
) ,
] ,
} satisfies Meta < typeof SubIssuesWorkflowPanel > ;
export default meta ;
type Story = StoryObj < typeof meta > ;
export const Default : Story = { } ;