Repository navigation
Add optional error handoff to repository-local canvas boards - #5706
Conversation
Offer optional, redacted Send to agent requests with confirmed child-session outcomes across the four local boards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
🚦 Submission status: 👀 Ready for reviewRisk tier: Why this tier
Automated checks
Review
Commands
Updated for |
|
🟡 Contributor Reputation Check: MEDIUM risk
Maintainers: please review this contributor before merging. |
There was a problem hiding this comment.
🟡 Changes recommended
Confirmed security, handoff correctness, and accessibility defects remain unresolved.
8 open issues
Cookie redaction leaves credentials after the first semicolon · New Unbounded credential-key matching can freeze synchronous redaction · New Broad alert capture misclassifies user content as diagnostics · New Cancellation notices incorrectly offer repair actions · New Cancellation and invalidation notices incorrectly offer agent actions · New Duplicate errors create repeated actions and child-session requests · New Fixed error tray obscures drawer decision controls · New Error tray is inaccessible while modal or drawer focus traps are active · New
What changed in this PR
Adds optional Send to agent handoff to four repository-local canvas boards, leaving public canvases unchanged.
Changes:
- Introduces a shared error tray with diagnostic redaction and parent-mediated child-session confirmation.
- Integrates generated helpers and error markers into the existing boards.
- Adds synchronization tooling, regression coverage, and maintainer documentation.
| File | Description |
|---|---|
eng/sync-canvas-error-handoff.mjs |
Generates and checks local helper copies. |
eng/canvas-error-handoff/runtime.mjs |
Implements error capture, handoff requests, and confirmation. |
eng/canvas-error-handoff.test.mjs |
Covers redaction, requests, UI behavior, and synchronization. |
docs/maintainers/canvas-evidence-and-metrics.md |
Documents privacy, behavior, and maintenance. |
.github/extensions/resource-staleness-board/public/app.js |
Marks preview and resource errors. |
.github/extensions/resource-staleness-board/extension.mjs |
Connects SDK and server wrappers. |
.github/extensions/resource-staleness-board/error-handoff.mjs |
Adds the generated staleness-board helper. |
.github/extensions/open-pr-board/README.md |
Documents the optional handoff action. |
.github/extensions/open-pr-board/public/app.js |
Marks detail-loading errors. |
.github/extensions/open-pr-board/lib/server.mjs |
Uses the handoff server with an injectable factory. |
.github/extensions/open-pr-board/extension.mjs |
Configures SDK wrappers. |
.github/extensions/open-pr-board/error-handoff.mjs |
Adds the generated Open PR helper. |
.github/extensions/external-plugin-review-board/README.md |
Documents the optional handoff action. |
.github/extensions/external-plugin-review-board/public/app.js |
Marks issue-detail errors. |
.github/extensions/external-plugin-review-board/lib/server.mjs |
Uses the handoff server wrapper. |
.github/extensions/external-plugin-review-board/extension.mjs |
Configures SDK wrappers. |
.github/extensions/external-plugin-review-board/error-handoff.mjs |
Adds the generated plugin-review helper. |
.github/extensions/duplicate-review-board/public/app.js |
Marks background-review errors. |
.github/extensions/duplicate-review-board/extension.mjs |
Connects SDK and server wrappers. |
.github/extensions/duplicate-review-board/error-handoff.mjs |
Adds the generated duplicate-review helper. |
Review effort: Max
Keep capabilities in canvas URL fragments rather than HTTP bootstrap responses, and reject unauthorized dispatch and status requests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Redact complete cookie fields with bounded processing, deduplicate diagnostics across operations, exclude expected notices and rendered content, and keep handoff controls inside the active modal without covering recovery actions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved diagnostic privacy and handoff reliability defects prevent safe use of the new action.
9 open issues
Sanitize complete URLs before redacting embedded tokens · New Include untrusted-data and no-publish constraints in child prompts · New Exclude local cancellation notices from failure markers · New Exclude cancellation and content-change notices from failure markers · New Correlate interceptor and DOM failures under one stable incident identity · New Mount the tray inside active dialogs and drawers · New Enforce an overall deadline across start and status requests · New Retain pending error receipts instead of evicting them · New Use explicit failure markers instead of generic alert selectors · New
8 resolved since last review
Broad alert capture misclassifies user content as diagnostics Unbounded credential-key matching can freeze synchronous redaction Cookie redaction leaves credentials after the first semicolon Error tray is inaccessible while modal or drawer focus traps are active Fixed error tray obscures drawer decision controls Duplicate errors create repeated actions and child-session requests Cancellation and invalidation notices incorrectly offer agent actions Cancellation notices incorrectly offer repair actions
Review effort: Max
Sanitize URLs before token replacement, propagate child safety boundaries, retain receipts, deduplicate board error prefixes, and exclude expected guidance toasts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Security-sensitive server wrapping and child-session creation require fixes and end-to-end desktop validation before human approval.
5 open issues
JSON-encoded diagnostics bypass this credential matcher. For example, `JSON.stringify({ message:… · New If a refresh removes an issue whilegh.postCommentis pending, the subsequent… · New A stale approval or re-review request still offers Send to agent for an expected workflow conflict.… · New The drawer-column change handler still callstoast(error.message, { error: true })at line 828.… · New A stalecancel_workrequest reachesBoardStore.check, which still throws a plain error when its… · New
3 resolved since last review
Review effort: Max
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The child-session creation and confirmation flow crosses browser and agent boundaries but lacks end-to-end validation in the desktop runtime.
1 open issue
5 resolved since last review
JSON-encoded diagnostics bypass this credential matcher. For example, `JSON.stringify({ message:… A stalecancel_workrequest reachesBoardStore.check, which still throws a plain error when its… The drawer-column change handler still callstoast(error.message, { error: true })at line 828.… A stale approval or re-review request still offers Send to agent for an expected workflow conflict.… If a refresh removes an issue whilegh.postCommentis pending, the subsequent…
Review effort: Max
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve periodic refresh provenance and error handoff classification. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved credential redaction, receipt handling, inline-script insertion, notice classification, focus restoration, action-error reporting, and CI synchronization issues block approval.
9 open issues
Redact credentials in decoded JSON message strings · New Redact credentials in decoded JSON message strings · New Redact credentials in decoded JSON message strings · New Redact credentials in decoded JSON message strings · New Handle uncertain dispatch errors without rejecting child receipts · New Use literal replacement for inline configuration injection · NewAuthorization: token SYNTHETIC_CREDENTIALbecomes `Authorization: [redacted]… Exclude preview-loading prerequisite notices from handoffs · New Exclude stale bulk-approval conflicts from repair handoffs · New
2 resolved since last review
Review effort: Max
Preserve literal inline configuration and suppress expected approval preflights. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The security-sensitive handoff has unresolved defects, and real desktop child-session creation and receipt delivery remain unverified.
3 open issues
A balanced malformed JSON fragment such as `Request failed: {"message":"{\"token\":\"SYNTHETIC_CREDE… · New When a PR head changes between the board snapshot and the detail fetch, the local stale-head check… · New/startwaits forsession.sendto settle even whenrecord_error_handoffhas already stored a… · New
9 resolved since last review
Use literal replacement for inline configuration injection Handle uncertain dispatch errors without rejecting child receipts Redact credentials in decoded JSON message strings Redact credentials in decoded JSON message strings Redact credentials in decoded JSON message strings Redact credentials in decoded JSON message stringsAuthorization: token SYNTHETIC_CREDENTIALbecomes `Authorization: [redacted]… Exclude stale bulk-approval conflicts from repair handoffs Exclude preview-loading prerequisite notices from handoffs
Review effort: Max
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Critical credential leakage and the new cross-board dependency remain unresolved, alongside diagnostic-loss and notice-classification defects.
8 open issues
Redact bracketed URL credentials before text substitutions · New Redact bracketed URL credentials before text substitutions · New This import adds a new hard dependency from this board onopen-pr-board. Before this PR,… · New This is the same new dependency as inextension.mjs: the board's store now cannot load unless the… · New Redact bracketed URL credentials before text substitutions · New Redact bracketed URL credentials before text substitutions · New Redact complete URLs before embedded JSON substitutions · New The final text pass makes sanitized JSON invalid: a diagnostic containing `{"message":"Disk write… · New
3 resolved since last review
Review effort: Max
| const fragment = text.slice(start, end + 1); | ||
| let sanitized = fragment; | ||
| try { sanitized = JSON.stringify(sanitizeJson(JSON.parse(fragment), nesting + 1)); } | ||
| catch { sanitized = "[redacted invalid JSON diagnostic]"; } |
| const fragment = text.slice(start, end + 1); | ||
| let sanitized = fragment; | ||
| try { sanitized = JSON.stringify(sanitizeJson(JSON.parse(fragment), nesting + 1)); } | ||
| catch { sanitized = "[redacted invalid JSON diagnostic]"; } |
| import * as gh from "./lib/github.mjs"; | ||
| import { buildReviewPrompt, buildRereviewPrompt, REVIEW_FIELDS_SCHEMA } from "./lib/review-prompt.mjs"; | ||
| import { startBoardServer, expectedError } from "./lib/server.mjs"; | ||
| import { expectedNotice } from "../open-pr-board/lib/errors.mjs"; |
| import { mkdir, readFile, rename, writeFile } from "node:fs/promises"; | ||
| import { randomBytes } from "node:crypto"; | ||
| import path from "node:path"; | ||
| import { expectedNotice } from "../../open-pr-board/lib/errors.mjs"; |
| const fragment = text.slice(start, end + 1); | ||
| let sanitized = fragment; | ||
| try { sanitized = JSON.stringify(sanitizeJson(JSON.parse(fragment), nesting + 1)); } | ||
| catch { sanitized = "[redacted invalid JSON diagnostic]"; } |
| const fragment = text.slice(start, end + 1); | ||
| let sanitized = fragment; | ||
| try { sanitized = JSON.stringify(sanitizeJson(JSON.parse(fragment), nesting + 1)); } | ||
| catch { sanitized = "[redacted invalid JSON diagnostic]"; } |
| const fragment = text.slice(start, end + 1); | ||
| let sanitized = fragment; | ||
| try { sanitized = JSON.stringify(sanitizeJson(JSON.parse(fragment), nesting + 1)); } | ||
| catch { sanitized = "[redacted invalid JSON diagnostic]"; } |
| try { diagnostic = JSON.stringify(sanitizeJson(JSON.parse(raw))); } | ||
| catch { diagnostic = sanitizeEmbeddedJson(raw); } | ||
| return sanitizeText(diagnostic); |


Pull Request Checklist
npm startand verified thatREADME.mdis up to date.mainbranch for this pull request.Description
Handled canvas errors should be actionable without manually copying diagnostic details into another conversation. Adds an optional Send to agent action to the Open PR, External Plugin Review, Duplicate Review, and Resource Staleness boards under
.github/extensions/only. Public canvases underextensions/are unchanged.The additive error tray preserves existing messages, retry controls, and recovery behavior. Clicking dispatches a bounded, redacted diagnostic request, deduplicates repeated clicks, and displays success only after an actual child-session ID is confirmed. Dispatch and creation failures remain visible. A canonical runtime and generated local copies keep the four boards consistent; regression coverage and maintainer documentation describe the contract.
Type of Contribution
Additional Notes
The extension runtime does not expose direct child-session creation. Handoff therefore uses the supported parent-agent messaging path: the parent calls
create_sessionand reports the actual child ID or failure throughrecord_error_handoff. Message admission alone is never reported as creation. Real desktop child creation has not been exercised; browser checks used a mocked parent for confirmed success and failure. The Copilot integration checklist item remains unchecked for that reason. No new paid service integration is introduced, and this updates existing canvases rather than adding one.Validation:
node --experimental-vm-modules --test --test-reporter=spec eng\canvas-error-handoff.test.mjs eng\*board*.test.mjs eng\resource-staleness-*.test.mjs- passed, 278 tests after narrowing the scope.node eng\sync-canvas-error-handoff.mjs --check- passed, exactly four canvases.npm start- passed; generated README and marketplace remained unchanged.npm run plugin:validate- passed, 108 local plugins and 82 external entries.npm run website:data- passed.bash eng/fix-line-endings.sh- passed.git diff --cached --check- passed before committing.node --experimental-vm-modules eng\canvas-smoke-test.mjs --all --install never- failed on an existing Draw scanner false positive: unchanged private#requireis classified as a nonliteral require. That public canvas is outside this PR's scope.By submitting this pull request, I confirm that my contribution abides by the Code of Conduct and will be licensed under the MIT License.