53396f272a
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies. > - The issue list and issue detail surfaces summarize child/sub-issue progress for operators. > - Those summaries need to be compact and visually consistent because they appear in dense lists. > - The progress strip is most useful when there are multiple sub-issues to compare, so the summary intentionally stays hidden for a single sub-issue. > - This pull request tightens the sub-issue progress summary styling and updates the related tests. > - The benefit is a cleaner, more scannable task list without changing task ownership, status, or workflow behavior. ## What Changed - Adjusted sub-issue progress summary copy/styling in the issue list and detail summary helpers. - Intentionally render the progress summary only for two or more child issues; a single child issue still appears in the normal sub-issue list without a redundant progress strip. - Updated the UI tests that assert the rendered summary behavior. - Clarified the two-plus-child threshold in code with a named constant. ## Verification - `pnpm exec vitest run --project @paperclipai/ui ui/src/components/IssuesList.test.tsx ui/src/lib/issue-detail-subissues.test.ts` ## Screenshots  ## Risks - Low risk; this is a small UI presentation change with focused test coverage. - The intentional threshold change means parents with exactly one child no longer show the aggregate progress strip, avoiding redundant summary chrome while keeping the child visible in the list. - No schema or API behavior changes. > 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 model with tool use and local command execution; context window 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 - [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>
66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
import type { Issue, IssueStatus } from "@paperclipai/shared";
|
|
import { workflowSort } from "./workflow-sort";
|
|
|
|
export type SubIssueProgressTargetKind = "next" | "blocked";
|
|
|
|
export type SubIssueProgressTarget = {
|
|
issue: Issue;
|
|
kind: SubIssueProgressTargetKind;
|
|
};
|
|
|
|
export type SubIssueProgressSummary = {
|
|
totalCount: number;
|
|
doneCount: number;
|
|
inProgressCount: number;
|
|
blockedCount: number;
|
|
countsByStatus: Partial<Record<IssueStatus, number>>;
|
|
target: SubIssueProgressTarget | null;
|
|
};
|
|
|
|
export function shouldRenderRichSubIssuesSection(childIssuesLoading: boolean, childIssueCount: number): boolean {
|
|
return childIssuesLoading || childIssueCount > 0;
|
|
}
|
|
|
|
const MIN_CHILD_ISSUES_FOR_PROGRESS_SUMMARY = 2;
|
|
|
|
export function shouldRenderSubIssueProgressSummary(enabled: boolean | undefined, childIssueCount: number): boolean {
|
|
return enabled === true && childIssueCount >= MIN_CHILD_ISSUES_FOR_PROGRESS_SUMMARY;
|
|
}
|
|
|
|
export function buildSubIssueProgressSummary(issues: Issue[]): SubIssueProgressSummary {
|
|
const countsByStatus: Partial<Record<IssueStatus, number>> = {};
|
|
const progressIssues = issues.filter((issue) => issue.status !== "cancelled");
|
|
for (const issue of progressIssues) {
|
|
countsByStatus[issue.status] = (countsByStatus[issue.status] ?? 0) + 1;
|
|
}
|
|
|
|
const orderedIssues = workflowSort(progressIssues);
|
|
const nextIssue = orderedIssues.find((issue) => isActionableStatus(issue.status)) ?? null;
|
|
const remainingIssues = orderedIssues.filter((issue) => !isTerminalStatus(issue.status));
|
|
const blockedIssue =
|
|
nextIssue === null && remainingIssues.length > 0 && remainingIssues.every((issue) => issue.status === "blocked")
|
|
? remainingIssues[0]
|
|
: null;
|
|
|
|
return {
|
|
totalCount: progressIssues.length,
|
|
doneCount: countsByStatus.done ?? 0,
|
|
inProgressCount: countsByStatus.in_progress ?? 0,
|
|
blockedCount: countsByStatus.blocked ?? 0,
|
|
countsByStatus,
|
|
target: nextIssue
|
|
? { issue: nextIssue, kind: "next" }
|
|
: blockedIssue
|
|
? { issue: blockedIssue, kind: "blocked" }
|
|
: null,
|
|
};
|
|
}
|
|
|
|
function isActionableStatus(status: IssueStatus): boolean {
|
|
return status !== "done" && status !== "cancelled" && status !== "blocked";
|
|
}
|
|
|
|
function isTerminalStatus(status: IssueStatus): boolean {
|
|
return status === "done" || status === "cancelled";
|
|
}
|