* Fix sentry-triage canvas crash when the `sentry` package isn't bundled
Published awesome-copilot plugins ship extension source only, so the optional
`sentry` npm package the canvas depends on at runtime may be absent. Previously
that made the canvas crash on open instead of guiding the user through setup.
- Load the optional `sentry` package lazily and translate only the top-level
ERR_MODULE_NOT_FOUND for `sentry` into a package-missing setup state; any other
import failure (missing transitive dep, entrypoint throwing) is rethrown so a
real defect isn't masked behind a misleading "reinstall" message.
- Add a dedicated package-missing branch to the connection preflight and a
matching setup gate, kept distinct from the auth and transient-network gates so
the user never sees contradictory guidance. The canvas now opens and explains
what to do rather than crashing.
- Clear `configured` for the package-missing state so the status is no longer the
contradictory `configured:true` + `setup:'package-missing'`.
- Tell users to sign in with the package-local CLI via `npx sentry auth login`
run from the extension folder — the only form that resolves after a local
`npm install`, since a package-local binary isn't on the shell PATH.
- Update the README so the sign-in step and install guidance cover the
published-plugin layout (`com.github.copilot/extensions/sentry-triage`), not
just the standalone user/project extension paths.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Add an exact-slug Sentry project resolver to the sentry-triage canvas
The project picker previously only offered projects from the paged list the
canvas had already loaded. Teams with many projects (or a project outside the
first page) had no way to target one by slug. This adds a verify-on-commit
resolver: when a user types a slug that isn't a local match, pressing Enter
checks it against Sentry and only commits the canonical slug once verified,
so a scan never runs against an unverified or wrong-org project.
Canvas / UX (components/page.mjs, styles.mjs):
- Autocomplete accepts an exact slug not in the local list; Enter is the
explicit commit that triggers resolution (never an as-typed lookup).
- A visually-hidden aria-live region announces checking / verified / not
found / couldn't-check state, and the resolved state is rendered before
commit so screen readers hear the outcome.
- Footer/menu surfaces checking, prompt, missing, and error states, including
when local partial matches are present.
- Project choices are read from an org-keyed cache so a slug from a previously
selected org can never be treated as local after a free-text org switch;
the stale-completion guard also compares the org captured for the request.
Server / resolution (server.mjs, sentry.mjs, sentryClient.mjs, extension.mjs):
- CSRF-gated /api/resolve-project verifies a single slug against Sentry.
- Resolution runs on the shared serial request chain and is hardened against
Sentry outages and queue contention (transient errors are retryable, a
confirmed miss is cached as "missing").
Also bumps sentry-triage to 1.1.0 (package.json, plugin.json,
marketplace.json).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Harden sentry-triage repo/URL metadata validation and split issue/PR repos
The canvas relays model-reported GitHub artifact URLs and numbers (from a
turn that also ingests untrusted Sentry text) into tracking badges and the
"Fix with Copilot" flow. Validate that data against trusted, outside-the-model
anchors so injected text can't mint misleading badges or steer a write to the
wrong repo, and support issue/PR repos that legitimately differ.
- Add path-type-aware URL validation (repoRefNumber/urlInRepo): a URL must be
http(s), on the trusted host, in the expected owner/repo, and match the exact
artifact kind (/issues/<n> vs /pull/<n>) with a path-segment boundary after
the id. The id must be a positive safe integer. This rejects look-alikes like
/pull/123evil, cross-kind spoofs (an issue URL minting a "PR #N" badge), and
overflow/zero ids.
- Split the issue anchor from the PR anchor (deriveRepoAnchors): the tracking
issue and the fix PR can live in different repos. Cloud mode anchors the PR on
the issue/cloud repo; an explicitly selected local project anchors it on that
project's config-time repo (frozen at save time); "Current project" anchors it
on the trusted current-project repo from the git remote — never the issue repo.
A missing/malformed PR repo fails closed with a clear, actionable error.
- Derive tracking issue/PR numbers from the validated URLs (not the model), and
strip every pr* field when no concrete /pull/<n> in the PR repo is present, so
a badge can't point at a different artifact than the one verified.
- Freeze the selected project's repo into PR-target config at save time
(page.mjs/state.mjs/server.mjs) and include mode + local.repo in the
repo-change identity so switching projects re-derives annotations.
- Roll the optimistic rescan overlay back on a failed /api/refresh POST instead
of leaving a blocking overlay up until the fallback timer expires.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Derive displayed numbers from validated URLs; isolate project enumeration
Address Copilot review on #2815.
Comment #2: every displayed issue/PR number is now re-extracted from the
same URL that was validated against the expected repo, instead of trusting
the model-reported *Number field. Fixes the skipped/dedup, done, and
submit_work_pr paths so a reply pairing /pull/1 with prNumber 999 can no
longer render "PR #999" linking to PR 1. Numbers with no concrete anchor
(placeholder repo / non-GitHub tracker) keep the model value.
Comment #1: the projectId->repo mapping is a security anchor (it decides
which repo fix-session PRs are validated against), so it must not be
produced in a turn that also ingests untrusted Sentry titles. Enumeration
now runs in its own Sentry-free turn (loadProjectOptions), and the save
path re-binds the repo/name server-side from the selected projectId against
trusted project state; the browser no longer supplies the auth repo.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Demote sentry-triage project map from an authorization anchor
The projectId->repo map returned by the model's list_projects tool was
being used as the trust anchor for fix-session PR validation, and was
isolated into a separate "project enumeration" turn to keep untrusted
Sentry titles out of the prompt that produced it. But that turn joins the
same foreground conversation, so it is not a real trust boundary.
The SDK exposes no isolated model context and no trusted project-
enumeration API, so instead stop treating the model-relayed repo as an
authorization anchor at all. An explicitly selected project is authorized
by its host-resolved project_id (a trusted handle at create_session time),
so deriveRepoAnchors now returns prExpectedRepo='' + prProjectSelected for
that case and the preflight/URL gates skip the unbackable repo check. The
map is now display/dedup-search only, so enumeration is folded back into
the triage round-trip and the separate turn is removed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Fix fallout from demoting the sentry-triage project map
Follow-ups from review after the project-map demotion:
- Reject stale/forged local project IDs in set-pr-config. An unknown
non-empty projectId left boundProject null but was still stored, and
deriveRepoAnchors treats any non-empty id as an explicit selection —
which skips the missing-repo/PR-URL preflight. Store the id only when it
resolves to a known project, so an unresolvable id fails closed to ''
(Current-project mode, which requires a trusted git remote).
- Restore tracked-PR badges for split-repo selected projects. The tracked
badge validated the model-reported PR URL against the now-empty trusted
PR anchor, so repoRefNumber always returned null and every discovered PR
was stripped. The badge is read-only display, not a write gate, so anchor
its search + URL/number validation on the model-declared project repo
when there is no trusted anchor (still pins number->URL and host).
- Update set-pr-config and page.mjs comments: the bound repo/name are
display + dedup-search metadata, not PR authorization, and drop the stale
loadProjectOptions / "Sentry-free turn" references.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Fail closed on an unconfirmable dedup skip for selected projects
After demoting the project map, prExpectedRepo is '' for an explicitly
selected project, so the dedup "already being worked on" skip accepted a
bare model-reported PR number as authoritative. Because the dedup result
and the project list come from the same Sentry-bearing model turn, injected
text could forge a skip to both suppress the trusted host-resolved
create_session flow and surface an arbitrary PR link.
An authoritative skip suppresses the trusted flow and surfaces a PR link,
so it now requires a trusted PR-repo anchor: a URL-bearing PR validated
against a concrete prExpectedRepo. A selected project has no trusted anchor,
so its skip is unconfirmable and fails closed to a retryable error instead
of being honored. The downstream dedup validation is now only reached with a
concrete PR anchor, so its dead no-anchor branches are removed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Anchor Current-project PR repo to the checkout cwd, not GITHUB_REPOSITORY
runtimeDefaults.repo prefers GITHUB_REPOSITORY, which is the configured ISSUE
target and may point at a separate cloud repo; seedDefaultsFromSession() does not
overwrite an explicit GITHUB_REPOSITORY. All three deriveRepoAnchors calls passed
that value as currentProjectRepo, so when the issue repo differed from the driving
checkout, Current-project mode gated PR validation and dedup on the issue repo and
rejected legitimate PRs from the current project.
Derive the Current-project anchor unconditionally from the resolved session cwd via
currentCheckoutRepo(defaults) = repoFromPath(localPath), which co-derives from the
same git remote as the trusted host, and use it at all three anchor derivations.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Restrict local Copilot handoff to the current project
Remove the model-relayed "selected project" local write-handoff and its
whole plumbing (the Settings project dropdown, submit_projects/list_projects
enumeration, projectOptions state, and the server-side projectId re-bind).
The dropdown was the only thing feeding a model-supplied project_id into
create_session, and the only local mode whose PR repo came solely from the
untrusted Sentry triage turn. That made it both the write-steering vector the
reviewer flagged (server.mjs set-pr-config / deriveRepoAnchors) and the one
local mode where a dedup "skip" could never be verified against a trusted repo.
Local "Work on selected" now always runs the fix session in the CURRENT
project (the canvas's own checkout, host-trusted from its git remote), so the
PR repo is always a trusted anchor and dedup is always verifiable. Cross-repo
work goes through Cloud mode, whose repo the user types directly in Settings.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Published awesome-copilot plugins ship extension source only, so the optional
`sentry` npm package the canvas depends on at runtime may be absent. Previously
that made the canvas crash on open instead of guiding the user through setup.
- Load the optional `sentry` package lazily and translate only the top-level
ERR_MODULE_NOT_FOUND for `sentry` into a package-missing setup state; any other
import failure (missing transitive dep, entrypoint throwing) is rethrown so a
real defect isn't masked behind a misleading "reinstall" message.
- Add a dedicated package-missing branch to the connection preflight and a
matching setup gate, kept distinct from the auth and transient-network gates so
the user never sees contradictory guidance. The canvas now opens and explains
what to do rather than crashing.
- Clear `configured` for the package-missing state so the status is no longer the
contradictory `configured:true` + `setup:'package-missing'`.
- Tell users to sign in with the package-local CLI via `npx sentry auth login`
run from the extension folder — the only form that resolves after a local
`npm install`, since a package-local binary isn't on the shell PATH.
- Update the README so the sign-in step and install guidance cover the
published-plugin layout (`com.github.copilot/extensions/sentry-triage`), not
just the standalone user/project extension paths.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Add Git Worktree Explorer canvas
Add an interactive repository, worktree, branch, and commit graph with GitHub PR enrichment, safe inspection actions, and shared lane visualization.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 69dd5824-7094-4b82-8cfa-83c2fbe53307
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Address review feedback for Git Worktree Explorer
Security
- Quote copied shell commands (git -C / git log) with shell-safe single quoting and --end-of-options via new public/shell-quote.mjs
- Move repository path into the untrusted data block of the commit Ask Copilot prompt
Performance
- Compute branch divergence with a single `git for-each-ref %(ahead-behind:...)` query (Git 2.41+), falling back to rev-list with bounded concurrency (8) instead of one process per branch
- Drop the unused `git show --stat` summary from commit details
Correctness
- Only attach same-repository pull requests to local branches (filter isCrossRepository / headRepositoryOwner)
- Resolve the default branch by full name or upstream instead of a suffix match (server + client)
- Preserve already-loaded commit pages when a load-more request fails; only reset requests show the error state
- Size the lane graph from the widest row (lanes + branch badges + text) and collapse >3 branch tips into a "+N more" badge
Accessibility / UX
- Add aria-pressed to view toggles and graph nodes; replace the unimplemented ARIA tree with group/button semantics
- preventDefault on Space/Enter for branch badges so keyboard activation does not scroll
- Use a lane palette that meets 3:1 contrast in both light and dark themes (tested)
- Mobile inspector no longer auto-opens on snapshot load and gains a close button
Tests: 19 -> 29
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
* Migrate git-worktree-explorer to plugins/ manifest layout
Move the manifest from extensions/git-worktree-explorer/.github/plugin/plugin.json to plugins/git-worktree-explorer/plugin.json with README and copilot-extension.json, matching the extensions-container migration (#2334). Regenerated marketplace.json and docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
* Address follow-up review: shell-specific quoting and sibling row controls
- quoteShellArg/formatShellCommand take a target shell; PowerShell doubles apostrophes while POSIX uses '\\''. app.js detects the platform and reports which syntax was copied.
- Commit rows no longer nest branch badge buttons inside the row button; the row button and badges are sibling controls.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
* Make the closed mobile inspector inert and restore focus to the graph
When the inspector overlay is dismissed on narrow viewports it is now marked inert/aria-hidden so its controls leave the tab order and accessibility tree, focus returns to the selected graph control, Escape closes it, and viewport changes re-sync the state.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Aaron Powell <me@aaron-powell.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 69dd5824-7094-4b82-8cfa-83c2fbe53307
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
* Add worktree context collector
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Fix Where Was I worktree state
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Collect git graph and change details
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Add worktree git graph
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Add uncommitted change inspector
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Release Where Was I 1.1.0
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Normalize diff paths on Windows
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Fix change inspector clicks
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Collapse base branch graph history
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Keep diff drawer across context renders
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
* Fix Where Was I review feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c161d734-edb5-4707-a48c-966d4e83a2cf
* Address follow-up Where Was I review
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c161d734-edb5-4707-a48c-966d4e83a2cf
* Harden Where Was I canvas against title injection and fix graph/diff edge cases
- Remove inline onclick handlers; PR/issue cards are buttons wired with addEventListener
from data, the refresh/resume buttons use ids, and the page is served with a nonce-based
CSP (script-src 'nonce-...', default-src 'none'). escapeHtml now also escapes apostrophes.
- Collapse only the graph suffix after the final branch commit (splitCommitGraph, computed
server-side as baseGraphStart) so --topo-order interleaving never hides worktree commits.
- Preserve the selected status record through the UI, /file-diff, and get_file_diff so a path
that appears twice (e.g. staged deletion + untracked re-creation) previews the right record.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d224e01-e53f-46f8-beb2-0b8b64c5ee89
Copilot-Session: c161d734-edb5-4707-a48c-966d4e83a2cf
Copilot-Session: 18350039-5e2b-40f0-b537-bc22cabcbb1b
An on-call error-triage canvas for GitHub Copilot: scans live Sentry
issues, groups them by urgency, and hands issues off for tracking or a
fix PR. Registers a reusable canvas extension under extensions/ with a
matching plugins/sentry-triage manifest.
The extension runs a loopback HTTP/SSE server per canvas instance
(host-allow-listed, per-instance capability token never broadcast over
SSE), renders the triage board server- and client-side, and drives
model turns over a single shared Copilot session serialized so scan and
work turns can't interleave. Work state is prototype-pollution-guarded
against agent-controlled tracking keys, and sendAndWait timeouts are
handled without duplicating external side effects.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* feat: add fail-closed Local Delegation to Cairn dashboard
Port the-workshop Local Delegation seam into signals-dashboard 0.3.0.
Orthogonal off/on control beside repo/connected profiles; enable only when
local-agent-delegation is installed and a qualified route receipt is present.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: address Local Delegation review (skill path + README catalog)
- Walk marketplace/plugin and _direct install roots for local-agent-delegation
- Update plugins/signals-dashboard README and regenerate docs/README.plugins.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: keep Local Delegation Windows launches from reparsing -i prompt
Long LD orientation text split under wt/cmd on Windows (0x80070002). Keep
-i short/quote-free; rely on WORKSHOP_LOCAL_DELEGATION=enabled (+ skill)
for policy. Add charset/length guard tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: surface Local Delegation effective state on open toast and badge
Keep -i short with one optional ASCII line. Show operators
"Local Delegation effective · route <id>" on open and in the summary control.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: address Local Delegation GHCP review comments
- Cross-platform path.join in receipt fixture
- Atomic .local-delegation.json write (no symlink follow)
- Case-insensitive WORKSHOP_LOCAL_DELEGATION env clear
- Windows Terminal: set/clear env via cmd before agent
- aria-pressed on Local toggle
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: store Local Delegation preference user-locally, not in the workshop repo
A cloned workshop must not be able to ship preference:on. Key operator intent
under ~/.copilot/workshop-local-delegation/ by canonical workshop path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: preserve path case in Local Delegation preference key
Lowercasing collided distinct workshops on case-sensitive filesystems.
Normalize separators only; realpath already supplies a stable path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: allow parentheses in quoted Windows Local Delegation launches
Args are quote-wrapped; only block % and ! expanders inside quotes so paths
like C:\Work\Project (1) still launch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* fix: apply quoted-arg Windows safety guard in launch path
Complete the parentheses fix: use isSafeQuotedWindowsCmdArg for cmdSafe.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
---------
Co-authored-by: Jenny Ferries <jenny.ferries@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Michael Recachinas <mrecachinas@github.com>
Copilot-Session: c39b7696-c854-40c2-b2d6-cce84ba09750
* feat: add Windows app storage inspector canvas
* Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: address storage inspector review findings
* Correction to package and plugin
* fix: harden storage inspector cleanup
* Harden cleanup operation outcomes
* Add select a file or folder path in the result tabs to navigate the treemap to its deepest visible parent folder.
* Fixes to ensure selftesst pass
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Adopt the Agent Plugins v1.0.0 namespaced composition model from #2546. Bundle the reusable daily-focus-board canvas into Ember only and remove its obsolete standalone catalog manifest.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 52be9c67-3ae4-4610-93d0-fe0b7ab95ccb
An array is typeof "object", so progress:[] (or a top-level array state file) was kept as the container; JSON.stringify then drops the non-index props (counters, t, day...), the API returns progress:[] and the canvas crashes reading state.counters. Guard both with !Array.isArray, matching looksLikeBoard. Headless regression tests added (48/48).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fd1eae93-cc9f-4777-812c-a2a9872e1c2b
A state file with a non-string txt (e.g. {"txt":1}) previously loaded, then esc(n.txt) in the canvas threw (numbers have no .replace) and the whole board failed to render. Filter day/brain entries to require a string txt, mirroring the existing task-notes filter. Verified with a headless regression test (45/45).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fd1eae93-cc9f-4777-812c-a2a9872e1c2b
Resolve the .codespellrc conflict by retaining both the daily-focus-board checkin key and upstream ACI term.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fd1eae93-cc9f-4777-812c-a2a9872e1c2b
* Add flight-map-canvas extension
A canvas port of the Flight Map VSCode extension: a first-person
satellite terrain map flown with flight simulator controls, for
session breaks while an agent works.
The simulator under game/ is copied verbatim from the source
extension's media/ folder. That page already reached its host through
one seam - an acquireVsCodeApi() object and a placeholder in its head -
so extension.mjs fills that seam for the canvas: a loopback server that
injects a policy, the render configuration, and a shim translating
Server-Sent Events into the messages the page already handles.
Two agent actions: fly_to sends the flight to a capital, a geocoded
city, or a raw lat/lng, and picks a random capital when called with no
input; report_job shows the current job step under the HUD.
Adds the vendored three.min.js to the codespell skip list, matching the
existing entry for arcade-canvas's phaser.min.js.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Apply suggestions from code review
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* Add PR Artifact Explorer canvas
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 33fefd65-ed18-4eda-9c7d-48008f4a9c9d
* Address PR #2341 review feedback and codespell CI failures
PR review fixes:
- zip.mjs: bounded streaming inflate for readZipEntry (output cap = entry.uncompressedSize) to prevent memory exhaustion from malformed entries
- zip.mjs: new bounded verify Transform in streamZipEntry that enforces decompressed byte cap and verifies CRC32 on flush
- zip.mjs: new readEntryPrefix() helper that inflates only up to a byte cap for indexing use cases
- preview.mjs: move URL/path parsing inside the try block so URIError becomes a 400 response instead of an unhandled rejection
- cache.mjs: buildMetadata uses bounded readEntryPrefix (8 KiB) instead of full readEntry+slice, eliminating unbounded decompression during indexing
- cache.mjs: clearArtifactCache aborts and awaits in-flight downloads via AbortController before removing cache dirs so clear cannot be repopulated
- server.mjs / preview.mjs: track static preview servers by canvas origin and stop them on canvas close, preventing loopback server leaks
- trx-preview.js: prefer authoritative ResultSummary.outcome when it is a recognized TRX value; only infer from counters when unknown
- extension.mjs: set_account validates the requested id resolves to the active account before persisting; unknown ids now return an error
- server.mjs: reject progressive-pull offsets that are not multiples of PROGRESSIVE_PULL_BATCH_SIZE to prevent caching incomplete pulls as complete
- README.md: remove Markdown-rendering claim (files are shown as escaped text)
CI fix:
- .codespellrc: skip vendored asciinema-player(*.min.js) and primer-*.css bundles
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 47369ff4-859a-425f-8a47-763cc1a5f25f
* Address second round of PR #2341 review feedback
- github.mjs: request isDraft in the pullRequestSignals GraphQL enrichment and propagate draft into search-sourced pulls (search REST API has no draft field)
- cache.mjs: give each writeJsonAtomic write a unique temp filename via a module sequence and clean up the temp file on failure to avoid concurrent-write races
- cache.mjs: deleteCachedArtifact now aborts and awaits any in-flight download for the artifact before removing files; internal mismatched-metadata purge uses a raw removeArtifactFiles helper to avoid aborting its own operation
- accounts.mjs: skip non-github.com CLI accounts before reading tokens so GitHub Enterprise Server credentials are never sent to api.github.com
- preview.mjs: require the requested entry to be the root index.html before launching a static preview (previously any nested HTML file could start one)
- zip.mjs: validate the EOCD comment length ends exactly at the archive tail so a file comment containing the EOCD signature is not mistaken for the record
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 47369ff4-859a-425f-8a47-763cc1a5f25f
* Address remaining PR artifact explorer feedback
Fix malformed preference normalization, replace artifact-presence request fanout with cached repository artifact pagination, restrict static previews to the root index, and derive completed TRX outcomes from counters. Add focused regression coverage for all four behaviors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db486df1-17f1-4623-ac7e-61eff2c76da4
* Fix PR artifact explorer review feedback
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6ee2bc81-2114-4b1f-987a-cb47ea35e132
* Stabilize artifact discovery and cache cleanup
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6ee2bc81-2114-4b1f-987a-cb47ea35e132
---------
Co-authored-by: David Pine <7679720+IEvangelist@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 33fefd65-ed18-4eda-9c7d-48008f4a9c9d
Copilot-Session: 47369ff4-859a-425f-8a47-763cc1a5f25f
Copilot-Session: db486df1-17f1-4623-ac7e-61eff2c76da4
Copilot-Session: 6ee2bc81-2114-4b1f-987a-cb47ea35e132
Resolves the Copilot review on board-core.mjs: task ids like `toString` /
`constructor` passed validId and, used as object keys, could resolve to
Object.prototype members. Now validId also rejects any inherited name (`s in {}`),
and the progress maps (counters, t) are null-prototype so a task id can never
resolve to an inherited member. (`__proto__` was already blocked by the id grammar.)
Headless test 43/43 (adds inherited-name rejection, drop-on-normalize, and an
Object.prototype-intact check).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Resolves two more Copilot review comments on board-core.mjs:
- Mutations no longer report false success: every op returns an explicit error
for unknown task ids, wrong task kinds, and empty/no-op inputs, and mutate
propagates it (so set_status on a bad id fails instead of claiming { ok: true }).
- The default state filename is now date-scoped (focus-board-<YYYY-MM-DD>.json),
so reopening the board tomorrow starts a fresh day instead of silently reusing
today's tasks/progress. Explicit stateFile paths are used as-is; input doc updated.
Headless test 39/39 (adds op-error + date-scope cases).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Resolves the remaining Copilot review comments.
Canvas extension (board-core.mjs, board.html):
- DNS-rebinding: pin the Host header to the exact 127.0.0.1:<port> authority and
require a per-server capability token (minted at startup, embedded in the served
page, sent as x-board-token) on ALL /api/* routes -- so GET /api/state can't leak
task data and POSTs can't be forged. Mirrors extensions/signals-dashboard.
- Destructive write: loadDoc only synthesizes a fresh board for ENOENT and now
propagates I/O + JSON parse errors, so a transient/malformed state file is never
overwritten by a later mutation.
- XSS: escape emoji (from the seed / add_task action) at render, like title/unit.
Skill (board.template.html, sample-board.html):
- a11y: each task card gets role=group + aria-label so screen readers get task context.
- counters: step=1 on the goal/update number inputs to match the positive-integer contract.
Verified headless (35/35): token gates reads+writes, CSRF + foreign-Host refused,
malformed file left intact. Repo plugin + skill validation green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Replaces the generated placeholder with an actual rendered board (cropped to the
visible cards). Closes the last gap flagged in the review.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Addresses the Copilot review on board-core.mjs: an externally supplied
stateFile pointing at an existing non-board file was silently normalized to an
empty board and overwritten on first mutation. ensureStateFile now refuses an
existing file unless it parses as JSON and looks like a board (a
'daily-focus-board' marker or the board schema); the marker is stamped on every
write. Also reserve 'day'/'brain' as task ids so a task can't collide with the
momentum/brain-dump feed delete routing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Adds a canvas version of the daily-focus-board alongside the skill, addressing
@aaronpowell's review suggestion. The canvas renders the board in the Copilot
app and is backed by a JSON state file the assistant reads and writes, so you
can mark tasks done, add tasks, log progress, and recap your day from chat --
the file-backed "close the loop" upgrade over the localStorage skill.
The skill stays as the zero-install universal fallback for anyone not in the
Copilot app (same both-not-either pattern as the-workshop's signals-dashboard).
- extensions/daily-focus-board/: extension.mjs (canvas + session wiring) +
board-core.mjs (loopback server, JSON state file, mutations, recap) +
assets/board.html (file-backed UI) + preview.png + manifests.
- plugins/ember: register via x-awesome-copilot.extensions, bump 1.1.0 -> 1.2.0,
add a Components row.
Note: assets/preview.png is a placeholder pending a real board screenshot.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Treat malformed authentication records as missing so browser sign-in can recover, preserve real file-read failures, and pin the Azure Identity persistence dependencies exactly.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Rewrite the extension documentation around the GitHub Copilot app experience, add official Connector Namespace guidance, and align marketplace metadata with the public-facing product description.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace Azure CLI-only authentication with InteractiveBrowserCredential, protected sign-in lifecycle endpoints, and subscription refresh after sign-in. Persist the Azure Identity cache securely across extension reloads and align connector restart guidance with the GitHub Copilot app.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sync signals-dashboard/extension.mjs to the-workshop@f3d2298.
- isInsideRoot: reuse the trailing separator when the workshop root is a filesystem root (/ or C:\\) so desks below it aren't rejected.
- Correct the launch guard comment (macOS single-quotes deskPath; Linux uses cwd).
- Replace the stale double-quote-rejection claim with the real containment + quoting guarantees.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Sync signals-dashboard/extension.mjs to the-workshop@91c09d7.
- Render the desk open button unconditionally.
- Drop the deskPath double-quote guard (path is quoted/cwd/single-quoted per platform; guard rejected valid POSIX paths).
- Escape CR/LF in AppleScript string literals.
- On Windows run the agent through cmd.exe /k so PATHEXT resolves .cmd shims (copilot/agency).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Synced from the-workshop@3cd8a0c6a1e24864d746fedb1f2f7ee4f3b17603. Round-4 review: remove the success timeout that could report an unconfirmed launch as launched:true; Node guarantees one of spawn/error fires.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Synced from the-workshop@591591c. Round-3 review: existsSync became dead after PATH validation moved to statSync/accessSync.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Synced from the-workshop@8106dd4. Addresses the second GHCP review pass on #2363:
- isOnPath now requires a runnable file (X_OK / PATHEXT), not just existsSync.
- launchDeskConsole adds a realpath-containment check (symlink escape) and takes workshopDir.
- Dashboard copies the path only on the non-launch fallback.
- Mutating /api/* routes now require the canonical loopback Host plus a per-server capability token (DNS-rebinding defense), mirroring connector-namespaces.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Addresses the Copilot review on this PR:
- deskAgentArgv: an explicit WORKSHOP_DESK_AGENT override is now
authoritative. =agency insists on the wrapper even when it isn't on
PATH; the PATH check is only used for automatic selection when unset.
- launchDeskConsole: restrict the desk name to a conservative slug
before it reaches any shell, and drop the double quotes from the
orientation prompt, so the cmd.exe `start` fallback can no longer be
turned into command execution by a desk directory named e.g.
"review&calc".
- macOS: drive Terminal via AppleScript to actually cd into the desk and
exec the agent (POSIX single-quoted), instead of only opening Terminal
and falsely reporting the CLI launched.
- Linux: pass the agent command after each emulator's exec flag, so the
desk comes up running its agent instead of a bare shell.
- Client openDesk: track whether the clipboard write succeeded and stop
claiming "path copied" when the browser blocked it.
Synced from the-workshop@8e22ce84b2b28d9c2205730b1cd52db8586a0416
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Brings the awesome-copilot copy of the signals-dashboard (Cairn) extension to parity with jennyf19/the-workshop@bc708fe. The desk cards' path button becomes an open button: clicking it launches a Copilot CLI in that desk's folder, oriented to read the journal and continue, mirroring the workshop app's console launcher. This keeps every desk inside the one workshop repo instead of spinning off a separate checkout.
The launcher prefers a wrapper agent CLI when one is on PATH (env-overridable via WORKSHOP_DESK_AGENT), and falls back to vanilla 'copilot' otherwise, so it works out of the box for everyone. New open_desk canvas action; /api/open now returns whether a terminal launched. plugin.json and assets/preview.png are unchanged.
Provenance: jennyf19/the-workshop@bc708fe905
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
- extension.mjs: serialize stash read-modify-write behind a per-desk mutex;
order signals by explicit ISO timestamp (clone-safe) not mtime; keep desks
with malformed latest signal as 'awaiting'; gate calibration badge on a real
gap value; add prefers-reduced-motion CSS; toast aria-live region; rename
open_desk -> get_desk_path (returns path, no session); handle server.listen errors.
- signal-write/SKILL.md: document timestamp + run_id, ordering guidance, and the
outcome (calibration) signal schema.
- workshop-ta.agent.md: a desk is a persistent workstream picked up by independent
sessions, not one long-running process.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
signals-dashboard imports '@github/copilot-sdk/extension' but shipped
without a package.json — the only SDK-importing extension in the repo
missing one (every peer that imports the SDK declares it). Without the
manifest the host has no dependency to resolve at load time. Add a
package.json matching the peer shape (type: module, main: extension.mjs,
'@github/copilot-sdk': latest — the modal peer convention).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Two follow-up review findings:
- signals-dashboard: the async createServer callback had no top-level
error boundary, so a malformed %-encoded path, a stash write on a
read-only workshop, or a scan failure rejected a promise the server
never awaits — hanging the request and risking an unhandled-rejection
crash. Wrap the handler and return a controlled 500.
- workshop-create: Path A treated finding a marker (desks/, CAIRN.md,
etc.) as 'just use it', an early stop that left partially initialized
workshops incomplete. Make it detection-only and continue scaffolding
whatever is missing, per the 'only add what's missing' principle.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Second review round on the Cairn canvas extension:
- XSS via numeric fields: self-assessment scores, quality rating, and
token counts are read from unvalidated agent JSON and interpolated
into HTML/style/title. Coerce them at the source in scanSignals via
toScore (clamped 0..5) and toCount (finite nonnegative int), and
esc() the effort label, so a nonnumeric value can neither inject
markup nor break layout/width.
- CSRF: /api/stash, /api/restore, /api/open are state-changing loopback
POSTs that previously accepted any origin, so a web page that guessed
the port could mutate .desk-stash.json. Add isCrossSiteRequest() and
reject cross-site POST /api/* (Origin / Sec-Fetch-Site check), mirroring
the loopback protection in connector-namespaces/server.mjs.
- Accessibility: the 5s auto-refresh replaced the whole #content subtree,
dropping keyboard focus. Skip the swap when markup is unchanged and
restore focus to the same desk/action button when it does change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
Address PR review findings on the Cairn canvas extension:
- XSS: replace inline onclick handlers (which used HTML-escape that
does not escape single quotes) with event delegation via
data-act/data-desk attributes and one document click listener that
survives the innerHTML auto-refresh.
- Path traversal: add isValidDeskName() and enforce it in every HTTP
and canvas-action handler that takes a desk name (reject empty, /,
\\, null byte, '.' and '..').
- Crash safety: String()-coerce in esc()/truncate() and guard
outcomeIssues with Array.isArray so a malformed signal cannot take
down the whole dashboard render.
- Honest UI: the per-desk button no longer claims to 'open' a desk;
it is relabeled 'path' and copies the desk's filesystem path to the
clipboard with an accurate toast built via textContent (not innerHTML).
- Correctness: outcome signals only pair with a signal when emitted at
or after it (within 1hr), and activeCount is computed by excluding
stashed desks instead of subtracting counts (no longer goes negative).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
The signals-dashboard extension was missing its required screenshot asset, so it failed awesome-copilot's validateExtensionManifest (logo must equal "assets/preview.png" and the file must exist) and never materialized -- meaning the Cairn canvas would not ship to the GHCP app. Adds the 1024x1024 preview.png and the convention logo field. Validated locally: node eng/validate-plugins.mjs -> extension signals-dashboard is valid; all 70 plugins + 19 extensions pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
The dashboard is the centerpiece — real-time agent coordination
view showing desk status, signal types, intent text, outcome
pairing with honesty gap, token usage, and stash/restore controls.
Includes all updates from the-workshop PRs #3-#7:
- Empty state guidance for new users
- Token usage display per desk
- TA partnership signals + Cairn awareness
- Intent-as-text (execution signals use descriptive text)
- Outcome signal pairing with honesty gap calibration
- Open desk button
- Subtype labels (done/checkpoint/blocked/hands-up)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26faf13e-639c-4a21-ac05-c0dc2bff7c62
* Migrate extension plugin materialization layout
Materialize extension plugins into a dedicated extensions/ container, validate the new manifest convention, and bump extension plugin versions.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Keep extension manifests source-compatible
Restore source extension manifests to "extensions": "." while preserving materialization-time rewrite to "extensions" in distribution output.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Validate canvas extension layout for external submissions
Add intake and quality-gate checks for canvas-tagged external plugins so they must include extensions/extension.mjs and optional manifest extensions is validated when present.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Fix plugin clean extension pass and typo guard text
Declare EXTENSIONS_DIR in clean-materialized-plugins and run extension cleanup once after plugin cleanup. Also normalize misspelled-key detection strings to satisfy spelling checks without changing validation behavior.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Proper codespell fix
* Separate canvas structure quality gate status
Track canvas structure as its own gate status and output, include it in aggregate summaries, and enforce Git object types so extensions/ is a tree and extensions/extension.mjs is a blob.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Move extension bundles during materialization
Change extension-plugin materialization to move root bundle entries into extensions/ instead of copying them, and assert originals are removed in test coverage.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Nest materialized extension bundles by plugin name
Materialize extension plugins into extensions/<plugin-name>/... (moved entries) so resulting paths are duplicated by design, and bump extension plugin versions to the next patch release.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Fix extension materialization publish and cleanup
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Preserve root extension logo asset
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Migrate extension plugin materialization layout
Materialize extension plugins into a dedicated extensions/ container, validate the new manifest convention, and bump extension plugin versions.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Keep extension manifests source-compatible
Restore source extension manifests to "extensions": "." while preserving materialization-time rewrite to "extensions" in distribution output.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Validate canvas extension layout for external submissions
Add intake and quality-gate checks for canvas-tagged external plugins so they must include extensions/extension.mjs and optional manifest extensions is validated when present.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Fix plugin clean extension pass and typo guard text
Declare EXTENSIONS_DIR in clean-materialized-plugins and run extension cleanup once after plugin cleanup. Also normalize misspelled-key detection strings to satisfy spelling checks without changing validation behavior.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Proper codespell fix
* Separate canvas structure quality gate status
Track canvas structure as its own gate status and output, include it in aggregate summaries, and enforce Git object types so extensions/ is a tree and extensions/extension.mjs is a blob.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d26008fb-9928-4ba7-b7c8-36f35320f7c1
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>