mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-15 12:28:10 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - A task can cause an agent to create more tasks. > - Those tasks can belong to other projects or have another parent. > - The subtask view does not show all work created from the current task. > - This pull request adds a Tasks tab with separate subtask and creation groups. > - Operators can follow created work without changing its parent or project. ## Linked Issues or Issue Description **Problem or motivation** Operators need to see all work that an agent creates while running for a task. Parentage alone does not describe this relationship. Legacy and native runs must follow the same rules. **Proposed solution** Show all subtasks in one section. Separately group tasks created from the source task by their current project, with a No project group when needed. A created subtask appears in both sections. Use saved run context and recorded creation activity to find the source task. **Alternatives considered** Making all created tasks children would change their hierarchy. Removing overlap between sections would hide the creation relationship. This change keeps the two memberships separate. **Roadmap alignment** This extends the existing Activity log & action attribution capability. It does not add a new roadmap area. Related PR: #9727 adds a stored source-task field and inbound attribution UI. This PR adds the outgoing task list using existing run and activity records and does not require that schema change. ## What Changed - Add a company-scoped createdFromIssueId filter to issue lists. - Save the actor run during task creation, including legacy child-helper calls. - Recover historical run attribution from creation activity when the origin run is absent. - Render the production Tasks panel with all subtasks and independently grouped created work. - Keep progress only for subtasks. Add folding, hover fades and project links. - Fetch all result pages and refresh on issue activity. Show load failures with Retry. - Add database, API, UI and pagination tests, design-guide examples and Storybook pages. ## Verification - Before rebase: 158 targeted tests passed. Workspace typecheck, UI/server builds, Storybook build and token gates passed. - After rebase: full workspace typecheck and build passed. The cursor fix passes 26 focused tests and UI/server typechecks. - The full local test command completed its general-server group with 10,563 passing tests and two failures: a missing native-runner fixture and a concurrency-test timeout. Building the fixture and rerunning both affected files passed all 41 tests. The local command stopped before its remaining groups; all corresponding GitHub test shards passed on the submitted head. - GitHub checks on commit3b4bf0bd8: 31 passed, including the aggregate CI gate, all server/workspace/browser test shards, typecheck, build, runner verification, canary dry run, policy and security checks. Two conditional Storybook jobs were skipped by the workflow. - Greptile reviewed commit3b4bf0bd8at 5/5 with no unresolved comments. - Open Storybook at UX Labs / Tasks Created From a Task / Full Task Page. Check that a created subtask appears in both sections. Fold each group and use a project link. Check the No project group and first-task arrival story. ## Risks - Old tasks without a saved origin run or attributed creation activity cannot be linked to a source. The code does not infer a source from a shared creator or a comment. - The new list filter reads run and activity records. Source, run, activity and result stay within the requested company. - Task creation now saves the actor run when no explicit origin run is supplied. Existing explicit origins remain unchanged. No database migration is required. ## Model Used - OpenAI GPT-6 through Codex. The runtime identifies the model family as GPT-6 but does not expose a more specific model ID or context-window size. Used reasoning, repository tools, code execution and browser inspection. ## 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 searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
42 lines
1.8 KiB
TypeScript
42 lines
1.8 KiB
TypeScript
import { sql } from "drizzle-orm";
|
|
import { alias } from "drizzle-orm/pg-core";
|
|
import { activityLog, heartbeatRuns, issues } from "@paperclipai/db";
|
|
import { isUuidLike } from "@paperclipai/shared";
|
|
import { unprocessable } from "../errors.js";
|
|
|
|
/** Creation provenance is independent of the task's current parent or project. */
|
|
export function createdFromIssueCondition(companyId: string, sourceIssueId: string) {
|
|
if (!isUuidLike(sourceIssueId)) {
|
|
throw unprocessable("createdFromIssueId must be a UUID");
|
|
}
|
|
const source = alias(issues, "creation_source_issue");
|
|
// Resolve source runs independently of the candidate tasks so the database
|
|
// can reuse this set, rather than scan all company runs for each task.
|
|
const originatingRuns = sql`
|
|
SELECT ${heartbeatRuns.id}::text FROM ${heartbeatRuns}
|
|
INNER JOIN ${issues} AS ${source} ON ${source.id} = ${sourceIssueId}
|
|
AND ${source.companyId} = ${companyId}
|
|
WHERE ${heartbeatRuns.companyId} = ${companyId}
|
|
AND coalesce(
|
|
${heartbeatRuns.nativeIssueId}::text,
|
|
nullif(${heartbeatRuns.contextSnapshot}->>'issueId', ''),
|
|
nullif(${heartbeatRuns.contextSnapshot}->>'taskId', ''),
|
|
nullif(${heartbeatRuns.contextSnapshot}->>'taskKey', '')
|
|
) IN (${source.id}::text, ${source.identifier})
|
|
`;
|
|
return sql<boolean>`
|
|
${issues.id} <> ${sourceIssueId}
|
|
AND (
|
|
${issues.originRunId} IN (${originatingRuns})
|
|
OR (${issues.originRunId} IS NULL AND EXISTS (
|
|
SELECT 1 FROM ${activityLog}
|
|
WHERE ${activityLog.companyId} = ${companyId}
|
|
AND ${activityLog.entityType} = 'issue'
|
|
AND ${activityLog.entityId} = ${issues.id}::text
|
|
AND ${activityLog.action} IN ('issue.created', 'issue.child_created')
|
|
AND ${activityLog.runId}::text IN (${originatingRuns})
|
|
))
|
|
)
|
|
`;
|
|
}
|