mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 19:20:39 +09:00
## 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>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
import type { Issue } from "@paperclipai/shared";
|
|
import {
|
|
buildSubIssueProgressSummary,
|
|
shouldRenderRichSubIssuesSection,
|
|
shouldRenderSubIssueProgressSummary,
|
|
} from "./issue-detail-subissues";
|
|
|
|
function issue(
|
|
id: string,
|
|
status: Issue["status"],
|
|
createdAt: string,
|
|
blockedByIds: string[] = [],
|
|
): Issue {
|
|
return {
|
|
id,
|
|
identifier: `PAP-${id}`,
|
|
title: `Issue ${id}`,
|
|
status,
|
|
createdAt: new Date(createdAt),
|
|
blockedBy: blockedByIds.map((blockerId) => ({ id: blockerId })),
|
|
} as Issue;
|
|
}
|
|
|
|
describe("shouldRenderRichSubIssuesSection", () => {
|
|
it("shows the rich sub-issues section while child issues are loading", () => {
|
|
expect(shouldRenderRichSubIssuesSection(true, 0)).toBe(true);
|
|
});
|
|
|
|
it("shows the rich sub-issues section when at least one child issue exists", () => {
|
|
expect(shouldRenderRichSubIssuesSection(false, 1)).toBe(true);
|
|
});
|
|
|
|
it("hides the rich sub-issues section when there are no child issues", () => {
|
|
expect(shouldRenderRichSubIssuesSection(false, 0)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("shouldRenderSubIssueProgressSummary", () => {
|
|
it("requires both the opt-in flag and multiple child issues", () => {
|
|
expect(shouldRenderSubIssueProgressSummary(true, 2)).toBe(true);
|
|
expect(shouldRenderSubIssueProgressSummary(true, 1)).toBe(false);
|
|
expect(shouldRenderSubIssueProgressSummary(false, 1)).toBe(false);
|
|
expect(shouldRenderSubIssueProgressSummary(true, 0)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("buildSubIssueProgressSummary", () => {
|
|
it("counts statuses and picks the first actionable issue in workflow order", () => {
|
|
const summary = buildSubIssueProgressSummary([
|
|
issue("3", "todo", "2026-04-03T00:00:00.000Z", ["2"]),
|
|
issue("1", "done", "2026-04-01T00:00:00.000Z"),
|
|
issue("2", "in_progress", "2026-04-02T00:00:00.000Z", ["1"]),
|
|
issue("4", "blocked", "2026-04-04T00:00:00.000Z"),
|
|
issue("5", "cancelled", "2026-04-05T00:00:00.000Z"),
|
|
]);
|
|
|
|
expect(summary.totalCount).toBe(4);
|
|
expect(summary.doneCount).toBe(1);
|
|
expect(summary.inProgressCount).toBe(1);
|
|
expect(summary.blockedCount).toBe(1);
|
|
expect(summary.countsByStatus.todo).toBe(1);
|
|
expect(summary.countsByStatus.cancelled).toBeUndefined();
|
|
expect(summary.target?.kind).toBe("next");
|
|
expect(summary.target?.issue.id).toBe("2");
|
|
});
|
|
|
|
it("waits on the first blocked issue when no remaining work is actionable", () => {
|
|
const summary = buildSubIssueProgressSummary([
|
|
issue("1", "done", "2026-04-01T00:00:00.000Z"),
|
|
issue("2", "blocked", "2026-04-02T00:00:00.000Z"),
|
|
issue("3", "cancelled", "2026-04-03T00:00:00.000Z"),
|
|
]);
|
|
|
|
expect(summary.target?.kind).toBe("blocked");
|
|
expect(summary.target?.issue.id).toBe("2");
|
|
});
|
|
});
|