Skip to content

Artifact Explorer: resilient jobs, browser emulator, Docs, sharing, and workflow re-run - #5068

Open
IEvangelist wants to merge 31 commits into
github:mainfrom
IEvangelist:ievangelist-artifact-explorer-resilient-canvas
Open

IEvangelist wants to merge 31 commits into
github:mainfrom
IEvangelist:ievangelist-artifact-explorer-resilient-canvas

Conversation

@IEvangelist

@IEvangelist IEvangelist commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request Checklist

  • I have read and followed the CONTRIBUTING.md guidelines.
  • I have read and followed the Guidance for submissions involving paid services.
  • My contribution adds a new instruction, prompt, agent, skill, workflow, or canvas extension file in the correct directory.
  • The file follows the required naming convention.
  • The content is clearly structured and follows the example format.
  • I have tested my instructions, prompt, agent, skill, workflow, or canvas extension with GitHub Copilot.
  • I have run npm start and verified that README.md is up to date. (npm run build passes.)
  • I am targeting the main branch for this pull request.

Description

Updates the Artifact Explorer canvas (extensions/pr-artifact-explorer, plugin and skill) with:

  • Resilience: retry and full restart; background download/index/extract jobs that survive request/view changes; ZIP64 and bounded central-directory handling; soft limit prompts (download, indexing, decompression, idle) that never stop work, with raised limits checked against live progress. Integrity and disk guards stay hard.
  • Sharing and skill: unified targets (repo, PR, issue, artifact, route) for sharing links and the AI skill.
  • Browser emulator: localhost/HTTP(S) preview with mobile/tablet/desktop presets, viewport vs. visual zoom, history/refresh, natural-language navigation, real Playwright/Chromium screenshots and device emulation (explicit setup, no silent install), keyboard hints.
  • Docs tab: built-in usage docs.
  • Artifact preview: universal Preview/Raw toggle, native video, JUnit viewer, HTML Browser action, per-render race guards, theme sync, open in default browser.
  • UX: copy title / copy link buttons, #number links to GitHub, compact Back/Forward route history, full PR-detail skeleton.
  • Workflow re-run: confirmation-gated re-run of the originating Actions run (all or failed jobs), permission- and eligibility-aware, original actor/SHA warning.

Type of Contribution

  • Update to existing instruction, prompt, agent, plugin, skill, workflow, or canvas extension.

Additional Notes

Verification: extension suite 92/92 (--test-concurrency=2), plugin:validate, skill:validate, npm run build, and line-ending normalization pass. Re-run behavior was tested only against mocked GitHub APIs; no real CI was triggered.

Screenshots

Workflow runs view (default filters: Hide PR runs, Has artifacts).

Workflow runs view, dark theme

Workflow runs view, light theme


By submitting this pull request, I confirm that my contribution abides by the Code of Conduct and will be licensed under the MIT License.

IEvangelist and others added 20 commits October 12, 2026 23:23
…browser preview, and sharing skill

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
…nit XML viewer

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
…contextual UI, media, JUnit

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Parse JUnit times strictly, showing unknown timings as Not recorded.
Scope video error listener to its live preview and distinguish
network/integrity from decode failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Per-term substring matching made 'Browser A' match every row via the 'a' term. Match the whole normalized phrase and cover it in the Chromium regression.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
…e, open in default browser

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
…und-trip

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
… teardown, preserve report state across Raw/Preview

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
…on permission lookup errors

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
@IEvangelist
IEvangelist requested review from a team as code owners October 14, 2026 02:54
Copilot AI balanced review requested due to automatic review settings October 14, 2026 02:54
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

🔒 PR Risk Scan Results

Scanned 58 changed file(s).

Severity Count
🔴 High 0
🟠 Medium 6
ℹ️ Info 0
Severity Rule File Line Match
🟠 package-exec-command extensions/pr-artifact-explorer/README.md 41 npx playwright-core install chromium
🟠 package-exec-command extensions/pr-artifact-explorer/assets/browser.js 558 &lt;details class="docs-details"&gt;&lt;summary&gt;Set up Chromium if capture is unavailable&lt;/summary&gt;&lt;p&gt;Setup is explicit; Explorer does not install anything silently. Run &lt;code&gt;npm install&lt;/
🟠 package-exec-command extensions/pr-artifact-explorer/capture.mjs 36 "2. Install the browser once: npx playwright-core install chromium",
🟠 package-exec-command extensions/pr-artifact-explorer/capture.test.mjs 41 assert.match(error.message, /npx playwright-core install chromium/);
🟠 unpinned-version-indicator extensions/pr-artifact-explorer/package-lock.json 11 "playwright-core": "^1.60.0"
🟠 unpinned-version-indicator extensions/pr-artifact-explorer/package.json 7 "playwright-core": "^1.60.0"
Skipped non-text or missing files
  • extensions/pr-artifact-explorer/assets/docs/continuing-job.png
  • extensions/pr-artifact-explorer/assets/docs/preview-controls.png
  • extensions/pr-artifact-explorer/assets/docs/preview-options.png

This is an automated soft-gate report. Findings indicate review targets and do not block merge by themselves.

@github-actions github-actions Bot added canvas-extension PR touches canvas extensions new-submission PR adds at least one new contribution plugin PR touches plugins skills PR touches skills labels Oct 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Target validation, browser shutdown concurrency, persistence reliability, and documentation/API mismatches remain unresolved.

0 open findings

Files not reviewed (1)
  • extensions/pr-artifact-explorer/package-lock.json: Generated file
Previously missed (7)

In code that hasn't changed since last review

Medium severity Await in-progress launches before closing

extensions/​pr-artifact-explorer/​capture.mjs:386

Wait for an in-progress launch before capturing #browser. Otherwise close() called while #ensure() is launching sees null, returns successfully, and the launch can subsequently install a live browser/page after the canvas was closed, leaking the Chromium process.

Medium severity Retry Windows rename when persisting job state

extensions/​pr-artifact-explorer/​jobs.mjs:313

Use the same bounded Windows rename retry as preference persistence (state.mjs:174-195). A transient EPERM/EBUSY here is swallowed into persistError with no retry, so recent job state may never reach disk and a restart cannot report an active job as interrupted.

Medium severity Retry Windows atomic replacement during persistence

extensions/​pr-artifact-explorer/​limits.mjs:160

Persisting with a single rename omits the bounded Windows retry already used for preferences (state.mjs:174-195). A transient EPERM/EBUSY leaves persistError set and the accepted limit only in memory, so it is lost after restart. Reuse a shared atomic-replace helper with the established retries.

Medium severity Align parser and browser zoom limits

extensions/​pr-artifact-explorer/​targets.mjs:157

The parser accepts zoom commands through 500%, but the browser implementation and public action schema cap zoom at 300%. Inputs such as zoom 500% therefore parse successfully and are silently clamped to 300% in the UI. Use one maximum consistently and update both error messages.

This issue also appears in the following locations of the same file:

  • line 254
  • line 262
  • line 266
Medium severity Reject out-of-range PR and artifact IDs

extensions/​pr-artifact-explorer/​targets.mjs:225

Reject an out-of-range number instead of returning a target containing null. open PR 9007199254740993 in owner/repo currently reaches this branch, positive() returns null, and the result later becomes a /null route rather than an input error; the artifact branch has the same problem.

Low severity Support or remove the undocumented section parameter

extensions/​pr-artifact-explorer/​README.md:84

This advertises a section parameter that the canvas action neither declares nor reads; it always opens #/docs. Either add section to the action schema and route to the requested docs section, or document the action as parameterless.

Low severity Document only supported open_target commands

skills/​pr-artifact-explorer/​SKILL.md:25

The documented open_target examples overstate what the canvas action supports. The action handler only executes back/forward/refresh when a controlled browser is active; zoom 150%, tablet, 1280x720, and screenshot parse as commands but are rejected instead of being dispatched. Either route these through the corresponding browser actions or document those action names rather than presenting them as valid open_target input.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 18, 2026 23:45
…t a pull request

Add a repository-level Workflow runs tab and run detail page so scheduled,
manual, push and release runs can be explored alongside PRs.

- GitHub API: listWorkflows, listWorkflowRuns, getWorkflowRun and lazy
  artifact counts; shared run/artifact mappers
- Server: /api/workflows, /api/runs, /api/runs/:id, /api/runs/artifact-counts
- Targets: run/runs kinds, Actions URLs, routes, sharing, natural language,
  artifact routes that keep ?run=
- UI: event/status/workflow/branch filters, hide-PR toggle, paging, artifact
  badges, run page reusing preview, Browser view, sharing and re-run
- Canvas actions open_runs and open_run; README, skill, Docs and metadata
- Tests for APIs, targets, server routes and the UI

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@IEvangelist
IEvangelist force-pushed the ievangelist-artifact-explorer-resilient-canvas branch from f05391c to af867a0 Compare October 18, 2026 23:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Browser lifecycle, viewport handling, and workflow-filtering defects remain unresolved.

6 open issues
1 file not reviewed
  • extensions/pr-artifact-explorer/package-lock.json: Generated file

Review effort: Balanced

Comment on lines +135 to +138
const timeout = new Promise((resolve) => {
timer = setTimeout(() => resolve(false), timeoutMs);
timer.unref?.();
});
Comment on lines +242 to +243
this.#browser = await launchChromium();
this.#viewport = resolveViewport(viewportOptions);
Comment on lines +382 to +384
const browser = this.#browser;
this.#browser = this.#context = this.#page = null;
const clean = await closeBrowserBounded(browser);

async navigate(url, viewportOptions = {}) {
const target = assertHttpUrl(url);
await this.#ensure(viewportOptions);
id: workflow.id,
name: workflow.name,
path: workflow.path,
ref: /\.ya?ml$/.test(file) ? file : String(workflow.id),
if (normalized.event) query.set("event", normalized.event);
if (normalized.status) query.set("status", normalized.status);
if (normalized.branch) query.set("branch", normalized.branch);
if (normalized.excludePullRequests) query.set("exclude_pull_requests", "true");
… runs UX pass and job panel layout

- Hide PR runs and Has artifacts are on by default (nopr=0 / artifacts=0 turn them off)
- Branch picker autocompletes real branches via /api/branches (default branch first, prefix search, dot-segment safe)
- Runs filters use the shared picker/toggle controls at one height; page shell renders
  immediately with a skeleton, retryable inline errors, accessible status, cached counts
- Job progress panel gets a bounded width, header, status pill, byte/speed/ETA detail,
  app-button styling, and toasts stack above it
- Docs, skill, tests updated

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 13:03
@github-actions github-actions Bot added awaiting-automation and removed ready-for-review Submission passed intake validation and is ready for maintainer review labels Oct 19, 2026
@github-actions github-actions Bot added requires-submitter-fixes Submission has quality-gate findings that submitter must fix before maintainer review and removed awaiting-automation labels Oct 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const ordered = /\d/.test(list[2]);
const items = [];
while (i < lines.length) {
const m = lines[i].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);
Comment on lines +4 to +24
import { startInstance, stopInstance } from "./server.mjs";

const ID = "server-test";
let entry;
let target;
let origin;

before(async () => {
target = createServer((req, res) => {
if (req.url === "/blocked") res.setHeader("x-frame-options", "DENY");
res.end("ok");
});
await new Promise((resolve) => target.listen(0, "127.0.0.1", resolve));
origin = `http://127.0.0.1:${target.address().port}`;
entry = await startInstance(ID, null, () => {});
});

after(async () => {
await stopInstance(ID);
await new Promise((resolve) => target.close(resolve));
});
Comment on lines +35 to +37
.replace(/\*\*([^*]+)\*\*|__([^_]+)__/g, (_m, a, b) => `<strong>${a ?? b}</strong>`)
.replace(/(^|[^*\w])\*([^*\s][^*]*)\*(?!\*)/g, (_m, pre, t) => `${pre}<em>${t}</em>`)
.replace(/(^|[^_\w])_([^_\s][^_]*)_(?!\w)/g, (_m, pre, t) => `${pre}<em>${t}</em>`)

test("closeBrowserBounded returns false quickly when close never settles", async () => {
const started = Date.now();
const clean = await closeBrowserBounded({ close: () => new Promise(() => {}) }, 150);
Comment on lines +727 to +731
const payload = await githubRequest(
token,
`/repos/${repositoryPath(repository)}/actions/workflows?per_page=100`,
);
return (payload.workflows ?? []).map((workflow) => {

test("closeBrowserBounded returns within its bound for a stuck browser and never throws", async () => {
const { closeBrowserBounded } = await import("./capture.mjs"); const started = Date.now();
assert.equal(await closeBrowserBounded({ close: () => new Promise(() => {}) }, 150), false);
…ension doesn't crash on start

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Copilot AI balanced review requested due to automatic review settings October 19, 2026 15:04
@github-actions github-actions Bot added awaiting-automation requires-submitter-fixes Submission has quality-gate findings that submitter must fix before maintainer review and removed requires-submitter-fixes Submission has quality-gate findings that submitter must fix before maintainer review awaiting-automation labels Oct 19, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Confirmed defects can freeze Markdown previews, overwrite user settings during tests, and silently discard workflow filters.

14 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Workflow filename selection silently drops invalid filters

extensions/​pr-artifact-explorer/​github.mjs:737

The picker emits workflow filenames that normalizeRunFilters rejects. For example, .github/workflows/nightly build.yml produces ref: "nightly build.yml", but selecting it silently drops the workflow filter and queries all repository runs. Check the same filename pattern here and fall back to the workflow ID when it does not match.

1 file not reviewed
  • extensions/pr-artifact-explorer/package-lock.json: Generated file

Review effort: Balanced

const ordered = /\d/.test(list[2]);
const items = [];
while (i < lines.length) {
const m = lines[i].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);
});

test("limits PUT validates kind and applies values", async () => {
const ok = await call("/api/limits", { method: "PUT", body: { kind: "zipEntries", value: 5000 } });
Copilot AI balanced review requested due to automatic review settings October 19, 2026 15:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Markdown rendering can freeze the canvas, and pagination makes the final permitted workflow-run results inaccessible.

14 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Unmatched backticks cause catastrophic regex backtracking

extensions/​pr-artifact-explorer/​assets/​markdown-preview.js:23

This regex backtracks heavily on unmatched backtick runs. In a bounded check, text followed by 8,192 backticks exceeded one second, while 2 MiB of ordinary text completed in 2 ms. Artifact Markdown is rendered synchronously on the canvas UI thread, so a small untrusted file can freeze the explorer despite the size cap. Replace this expression with linear-time delimiter scanning and add a regression for long unmatched runs.

Medium severity Pagination limit hides results beyond the 990th run

extensions/​pr-artifact-explorer/​github.mjs:688

At 30 runs per page, this limit exposes only 990 results. A search with 995 results disables Older on page 33 and clamps page 34 back to 33, leaving five runs inaccessible. Define a 1,000-result limit, derive the page ceiling with Math.ceil, and use the result limit for reachable and capped. Update the boundary tests to cover the partial 34th page.

1 file not reviewed
  • extensions/pr-artifact-explorer/package-lock.json: Generated file

Review effort: Balanced

The default Latest scope deduplicated artifacts by name before the status
filter ran, so a retry hid the failing run's artifacts. Filters now apply
before deduplication and status counts reflect the same scope.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72ee5a2d-bcbb-4e0f-b86f-6ad56d4fc033
Copilot AI balanced review requested due to automatic review settings October 19, 2026 18:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Markdown rendering can stall the canvas, and workflow pagination leaves the final results unreachable.

15 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Pagination cannot reach final results beyond the 990th run

extensions/​pr-artifact-explorer/​github.mjs:688

At 30 runs per page, this cap exposes only 990 results. For a search with 991–1,000 results, page 33 reports hasMore: false, and requests for page 34 are clamped to 33, so the final runs cannot be reached. Define a separate 1,000-result limit, derive the page count with Math.ceil(limit / RUNS_PER_PAGE), and use the result limit for reachable and capped. Update the pagination tests to cover the partial 34th page.

1 file not reviewed
  • extensions/pr-artifact-explorer/package-lock.json: Generated file

Review effort: Balanced

// Input is HTML-escaped first, so only markup produced here reaches the DOM.
function inline(text) {
const codes = [];
let out = esc(text).replace(/(`+)([\s\S]*?[^`])\1(?!`)/g, (_m, _t, code) => {

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

canvas-extension PR touches canvas extensions merge-risk:high new-submission PR adds at least one new contribution plugin PR touches plugins requires-submitter-fixes Submission has quality-gate findings that submitter must fix before maintainer review skills PR touches skills

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants