Files
PaperClipAI/tests/runner-e2e/github-review-ownership.ts
T
DottaandPaperclip 6d46851920 test(e2e): add attended GitHub review bot journey (#17457)
## 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>
2026-10-24 12:16:26 -07:00

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;
}