Commit Graph

3 Commits

Author SHA1 Message Date
Tim Mulholland 63c2527ace Accept nested extensions/<name>/extension.mjs in external-plugin canvas checks (#2403)
* 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>
2026-07-29 14:05:31 +10:00
Tim Mulholland 7351f9f692 Fix external-plugin quality gates for tag-name locators (#2397) (#2399)
runVersionMatchGate and runCanvasStructureGate read locator content with git show/cat-file. For a tag-name locator, `git fetch origin <tag>` only updates FETCH_HEAD and never creates refs/tags/<tag>, so `git show <tag>:...` died with 'invalid object name' and produced a false infra_error.

Read the primary locator via HEAD (already checked out during clone) and non-primary locators via FETCH_HEAD after fetching, instead of the bare locator. This handles SHAs, short tag names, and fully-qualified tag refs uniformly without classifying the locator.

Adds regression coverage for the tag-locator path in both gates.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-23 10:50:31 -07:00
Aaron Powell 40665c23b7 Migrate extension plugin materialization to extensions container (#2334)
* 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>
2026-07-17 18:04:03 +10:00