mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
## Thinking Path > - Paperclip manages agents and the work they perform. > - Product E2E checks verify real user paths through those agents. > - A recorded GitHub review-bot walkthrough had no repeatable registered case. > - App setup, automatic triggering, publication, and task completion need separate evidence. > - This pull request adds an explicit attended journey to the existing catalog and report tools. > - Reusable setup profiles and independent assertions preserve the same outcome across compatible targets. ## Linked Issues or Issue Description Refs: #17262. This case remains an explicit attended attached-target flow. It does not replace the shared deployment-target or paid-operations framework. **What existing behavior does this improve?** The Product E2E catalog can now select and retain evidence for GitHub review-bot setup and its first automatic review. **Current behavior** Recorded walkthroughs cannot be rerun as a fixed registered case. A published review alone can hide a failed run or unfinished task. **Proposed behavior** Create and install an owned App, save automatic new-PR settings, create a defective PR, and require correlated review delivery, an inline finding, a consistent check, a successful run, and a Done task. Retain failures and confirm owned-resource cleanup. **Reason and benefit** The case makes a useful external integration reproducible without treating setup, unit checks, or report rendering as proof of a completed live journey. ## What Changed - Add two explicit attended cells for an existing configured agent in local or Daytona execution. - Declare hosting, account, connection, and credential prerequisites through private setup bindings. - Add fixed browser controls, ownership checks, independent grading, and cleanup. - Package declared screenshots through the existing sanitizer, manifest, validator, and dashboard. - Reject operations-managed execution until budgeted routing is qualified. - Document hosted QA credential setup and evidence limits. ## Verification - Focused catalog, oracle, report, connection, and ownership checks: 127 tests passed. - Inherited-environment preflight regression failed against the previous commit and passed after the fix. Actual-run environment verification remains required. - Harness TypeScript check and full workspace typecheck passed. Full build passed. The local full-test attempt was interrupted after repeated embedded PostgreSQL startup failures; its exit code of zero does not establish a pass; Exact-head Linux CI passed the full test, typecheck, build, Runner, serialized, browser, and canary gates on `abbddeade848edefc8de75f11cc9008b2cd52764`. - A real hosted attempt published a rated review and inline finding. It failed when its native session timed out before the task reached Done. Cleanup passed. Full live qualification remains open. - Credential scan found no known credential values in the authored files or retained report corpus. ## Risks - The full case creates external test resources and uses the configured provider. It requires an explicit selection and current task authority. It is excluded from default and CI matrices. - Time and run limits are not a hard dollar cap. Missing dollar cost stays unknown. Fixture setup does not weaken budget policy. - Fresh personal OAuth, unattended authentication, runtime skill-read proof, and cross-host qualification are separate claims. - The selected live fixture needs environment/lifecycle recovery before it can adopt a longer provider deadline. ## Model Used OpenAI GPT-6 family through Codex, with reasoning, code execution, and browser tools. The exact deployment model ID and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used, including the available 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 described the issue in-PR following the enhancement template - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket ID - [x] I have run focused tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation - [x] I have considered and documented risks - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open findings - [x] I will address all Greptile and reviewer comments before merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
44 lines
3.0 KiB
TypeScript
44 lines
3.0 KiB
TypeScript
import type { ChatEndpoint } from "../../packages/shared/src/types/chat-channels.js";
|
|
import { ConnectionFailure } from "./connection-config.js";
|
|
import type { GitHubReviewJourneyConfig } from "./github-review-config.js";
|
|
import type { ConnectionApi } from "./connection-target.js";
|
|
|
|
/** The issue run source includes history beyond company list windows. Related
|
|
* activity can name a run for another task, so hydrate and check direct ownership. */
|
|
export async function readOwnedGitHubReviewRuns(api: Pick<ConnectionApi, "get">, scope: { companyId: string; agentId: string; issueId: string }) {
|
|
const rows = await api.get<Array<{ runId: string }>>(`/api/issues/${scope.issueId}/runs`);
|
|
if (!Array.isArray(rows) || rows.some(row => typeof row.runId !== "string" || !row.runId)) throw new ConnectionFailure("invalid_issue_run_inventory");
|
|
const runs = await Promise.all([...new Set(rows.map(row => row.runId))].map(id => api.get<Record<string, any>>(`/api/heartbeat-runs/${id}`)));
|
|
if (runs.some(run => run.companyId !== scope.companyId)) throw new ConnectionFailure("issue_run_company_mismatch");
|
|
return runs.filter(run => run.issueId === scope.issueId && run.agentId === scope.agentId);
|
|
}
|
|
|
|
export interface GitHubJourneyResources {
|
|
endpointId?: string; pullNumber?: number; branch: string; appName: string;
|
|
appId?: string; appSlug?: string; appSettingsUrl?: string; installationSettingsUrl?: string;
|
|
issueId?: string; runIds: string[];
|
|
}
|
|
|
|
/** Bind cleanup to GitHub-confirmed identity on this attempt's owned endpoint. */
|
|
export function captureOwnedGitHubApp(resources: GitHubJourneyResources, endpoint: ChatEndpoint, config: GitHubReviewJourneyConfig) {
|
|
if (!endpoint.botExternalId || endpoint.setup.github?.registrationStatus !== "completed") return false;
|
|
const app = endpoint.setup.github;
|
|
if (endpoint.id !== resources.endpointId || endpoint.companyId !== config.target.companyId || endpoint.provider !== "github"
|
|
|| endpoint.assignedAgentId !== config.agent.id || endpoint.botLabel !== resources.appName
|
|
|| !/^[1-9][0-9]*$/.test(endpoint.botExternalId) || !app.appSlug || !/^[a-z0-9-]+$/.test(app.appSlug)
|
|
|| endpoint.botUsername !== `${app.appSlug}[bot]` || app.appOwnerType !== config.github.ownerType
|
|
|| app.appOwnerLogin?.toLowerCase() !== config.github.appOwner.toLowerCase()
|
|
|| (resources.appId && resources.appId !== endpoint.botExternalId)
|
|
|| (resources.appSlug && resources.appSlug !== app.appSlug)) throw new ConnectionFailure("owned_github_app_binding_mismatch");
|
|
resources.appId = endpoint.botExternalId;
|
|
resources.appSlug = app.appSlug;
|
|
const owner = encodeURIComponent(app.appOwnerLogin!);
|
|
resources.appSettingsUrl = app.appOwnerType === "organization"
|
|
? `https://github.com/organizations/${owner}/settings/apps/${app.appSlug}`
|
|
: `https://github.com/settings/apps/${app.appSlug}`;
|
|
// Installation management is a different resource. Uninstalling is not proof
|
|
// that the owned App registration was deleted.
|
|
resources.installationSettingsUrl = app.managementUrl;
|
|
return true;
|
|
}
|