mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 19:35:04 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Paperclip uses a paid full-stack campaign to verify runner behavior across providers and environments. > - The campaign already creates an interactive report, workflow logs, and retained evidence artifacts. > - The merge job summary shows result totals but does not link to those resources. > - Reviewers must search several workflow jobs and artifacts to find the executed cells. > - This pull request adds direct and safe links to the exact campaign, each cell, the workflow logs, and the artifacts. > - The benefit is that a reviewer can inspect a result from the Actions summary with one click. ## Linked Issues or Issue Description **What existing behavior does this improve?** This improves the `Merge and enforce campaign result` summary in the `Runner Full-Stack E2E` workflow. **Subsystem affected** The runner E2E report generator and its GitHub Actions workflow are affected. **Current behavior** The summary lists each selected cell and its result. It does not link to the published campaign report, the workflow logs, or the evidence artifacts. **Proposed behavior** The summary includes a `View results` section. It links to the exact immutable campaign report, the workflow logs, and the artifacts. Each cell name links to its stable section in the campaign report. **Reason and benefit** The current summary does not show reviewers where to inspect the run. Direct links make the result evidence discoverable without manual URL construction or artifact searches. **Breaking changes** None. This change only adds links and stable HTML anchors to existing report output. **Additional context** Related: #12904. The cited successful campaign is [run 34026735033](https://github.com/paperclipai/paperclip/actions/runs/34026735033). ## What Changed - Add a safe URL builder for public campaign, workflow, and artifact links. - Add a `View results` section to the GitHub Actions campaign summary. - Link each summary table cell to its exact section in the immutable campaign report. - Add stable execution anchors to the generated dashboard. - Reject non-HTTPS, credential-bearing, malformed, and ambiguous link destinations. - Document the new links and their retention or publication timing. ## Verification - `pnpm test:e2e:runner:unit` — 116 tests passed. - `pnpm test:e2e:runner:typecheck` — passed. - `pnpm typecheck` — passed, including migration safety. - `pnpm build` — passed. - `pnpm exec prettier --check ...` for all changed files — passed. - `git diff --check origin/master...HEAD` — passed. - The full local server suite also ran. One unrelated macOS workspace-runtime file passed 157 tests and failed 4 existing path and port assumptions. Two failures compare `/var` with `/private/var`. Two failures cannot reserve a port outside a hard-coded range. This PR does not change that file or its dependencies. ## Risks - The immutable campaign link becomes available after the history publisher completes. The workflow and artifact links remain available while publication runs. - The artifact link requires GitHub access and follows the existing 30-day retention period. - Invalid configured URLs are omitted instead of being rendered into the summary. > 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 desktop agent with GPT-5. The runtime does not expose the context-window size. The agent used repository inspection, agentic reasoning, code execution, and GitHub CLI tools. ## 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/no-internal-issue-references`, `fix/sandbox-secret-resolution`, `feat/adapter-retry-backoff`) 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
61 lines
1.5 KiB
TypeScript
61 lines
1.5 KiB
TypeScript
export interface RunnerE2EHistoryPublicDestination {
|
|
prefix: string;
|
|
publicBaseUrl: string;
|
|
}
|
|
|
|
export function validateHistoryPublicDestination(input: {
|
|
prefix: string;
|
|
publicBaseUrl: string;
|
|
}): RunnerE2EHistoryPublicDestination {
|
|
const prefix = input.prefix.replace(/^\/+|\/+$/g, "");
|
|
const segments = prefix.split("/");
|
|
if (
|
|
!prefix ||
|
|
segments.some(
|
|
(segment) =>
|
|
!segment ||
|
|
segment === "." ||
|
|
segment === ".." ||
|
|
!/^[A-Za-z0-9._~-]+$/.test(segment),
|
|
)
|
|
) {
|
|
throw new Error(
|
|
"RUNNER_E2E_HISTORY_PREFIX must be a safe non-empty key prefix",
|
|
);
|
|
}
|
|
const publicUrl = new URL(input.publicBaseUrl);
|
|
if (
|
|
publicUrl.protocol !== "https:" ||
|
|
publicUrl.username ||
|
|
publicUrl.password ||
|
|
publicUrl.search ||
|
|
publicUrl.hash
|
|
) {
|
|
throw new Error(
|
|
"RUNNER_E2E_HISTORY_PUBLIC_BASE_URL must be a credential-free HTTPS URL",
|
|
);
|
|
}
|
|
return {
|
|
prefix,
|
|
publicBaseUrl: publicUrl.href.replace(/\/+$/, ""),
|
|
};
|
|
}
|
|
|
|
export function validateHistoryDestination(input: {
|
|
bucket: string;
|
|
prefix: string;
|
|
publicBaseUrl: string;
|
|
}) {
|
|
if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(input.bucket)) {
|
|
throw new Error("RUNNER_E2E_HISTORY_S3_BUCKET is not a valid bucket name");
|
|
}
|
|
return validateHistoryPublicDestination(input);
|
|
}
|
|
|
|
export function runnerE2ECampaignPublicUrl(
|
|
destination: RunnerE2EHistoryPublicDestination,
|
|
campaignId: string,
|
|
) {
|
|
return `${destination.publicBaseUrl}/${destination.prefix}/campaigns/${encodeURIComponent(campaignId)}/index.html`;
|
|
}
|