* Enforce external plugin ref/sha consistency
Extract shared ref/sha normalization and consistency checks into eng/lib and reuse them in intake plus quality gate flows.
Add a dedicated ref/sha consistency quality gate surfaced in PR/intake summaries, and add targeted tests for matching and mismatched refs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6afe21ad-eafa-4c90-a1f2-053dedac7625
* Address review: tree/blob ref errors and PR workflow ref/sha column
- resolveCommitShaAtReadRef: classify rev-parse failure as 'fail'
instead of 'infra_error' because a successfully-fetched ref that
doesn't dereference to a commit is a submitter problem, not infra.
- validateRemoteRepository (intake): treat HTTP 422 from the commit
endpoint as a submitter error; all other non-404 errors remain
transient warnings requiring maintainer re-run.
- external-plugin-pr-quality-gates.yml: add ref/sha consistency
column to the per-plugin quality table and failure details block.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6afe21ad-eafa-4c90-a1f2-053dedac7625
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6afe21ad-eafa-4c90-a1f2-053dedac7625
* Accept nested extensions/<name>/extension.mjs in external-plugin canvas checks
The external-plugin canvas structure check (quality gate) and intake
validation both hardcoded a flat extensions/extension.mjs entry point,
falsely rejecting the documented nested extensions/<name>/extension.mjs
layout that installs and runs fine.
Scan the extensions/ directory for a nested subfolder containing
extension.mjs while still accepting the flat form for backward
compatibility. Applied to both runCanvasStructureGate (git-object
lookups) and validateCanvasPluginMetadata (Contents API), keeping them
behaviorally aligned. Added regression coverage for both paths.
Fixes#2402
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Harden nested canvas extension detection after adversarial review
Address multi-model review findings on the nested canvas extension fix:
- Quality gate: enumerate extensions/ via 'git ls-tree -z' with spawnSync (NUL-delimited, untruncated) so large directories no longer drop the real entry past the 12KB output cap.
- Intake: decouple the flat extensions/extension.mjs check from the directory listing, require an array listing (Array.isArray) before treating it as a directory, and surface an unverifiable (warning) result instead of a false rejection when the listing or a nested lookup hits a transient API error.
- Add regression tests: nested entry beyond the legacy output cap (gate) and unverifiable/flat-still-accepted paths when the listing errors (intake).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Enumerate canvas extensions via a single recursive Git Trees call in intake
Address PR review: the Contents API caps directory listings at 1,000 entries and required one request per extension subfolder, so a nested entry beyond the cap could be falsely rejected (the same truncation class the git gate avoids) and large repos risked latency / rate-limit exhaustion.
Replace the per-subfolder Contents API enumeration with one recursive 'git/trees/<locator>?recursive=1' fetch and inspect 'extensions/extension.mjs' and immediate 'extensions/<name>/extension.mjs' paths locally. A truncated tree without a located entry point is reported as unverifiable (warning) rather than rejected, and refs are normalized so 'refs/tags/<tag>' resolves as a tree-ish.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Bound canvas extension discovery to plugin scope
Address reviewer feedback on unbounded scaling for untrusted/large repos:
- Quality gate: replace the per-candidate-directory git cat-file spawns in
locateCanvasEntryPoint with a single recursive git ls-tree over the
extensions subtree, classifying flat/nested entry points in memory. Process
count is now constant regardless of how many folders live under extensions/.
- Intake: stop fetching the recursive git tree from the repo root (which a
large unrelated monorepo can push past the Trees API truncation limit and
never validate). Walk to the plugin's extensions directory one level at a
time to resolve its tree SHA, then fetch only that subtree recursively, so
verifiability depends on the plugin's own size, not the whole repository.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>