Add pair programming plugin#43
Conversation
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
hal-eisen-adfa
left a comment
There was a problem hiding this comment.
Code On The Go plugin review — pair
Ran the /plugin-review workflow (build + security audit + submission rubric). Great engineering underneath — clean lifecycle teardown, canonicalized path handling, an authenticated LAN transport, and thorough docs — but there are submission-conformance blockers. Details inline; summary here.
🔴 Blockers
- Build fails from a clean checkout.
./gradlew assemblePluginfails at:compileReleaseKotlin:libs/shared.jardoesn't exist, and behind it the code callsIdeEditorService.showPeerCursor/hidePeerCursor/clearPeerCursorsandIdeProjectService.openProject(File), which aren't in the committedplugin-api.jar. The README/HTML confirm these live on the unmergedfeat/ADFA-4419branch. The plugin can't build against the repo'sstage-trackedlibs/as submitted — this is the primary blocker. native.codepermission with no native code. No.so/jniLibs/NDK C++ (and no QR/camera library is even declared), yetplugin.permissionsrequestsnative.code.- No in-app help. No class implements
DocumentationExtension— no Tier 1/2 tooltip entries, no Tier-3 offlineassets/help page, and the sidebar item has notooltipTag. - Missing
plugin.editor_tabsmanifest entry for the contributed editor tab.
🟡 Conditional
- Add
plugin.max_ide_versionfor a real compatibility range (onlyminis declared).
✅ Clean
- Security: path traversal is defended (
PathMapper.toLocalCheckedcanonicalizes + enforces the root boundary), the WebSocket server enforces a join token, no reflection, no committed secrets/keystores. - Resource discipline (6.2): the coroutine scope is cancelled in
dispose(), sockets are closed onstopSession(), streams use.use{}, andPairServiceLocator.shutdown()nulls thePluginContextholder. - HTML documentation (6.6): all required sections, light bg / dark text, English, top-level.
Rubric scorecard
| Clause | Verdict |
|---|---|
| 6.1 Compatibility | Partial (no max_ide_version) |
| 6.2 Resource discipline | Pass |
| 6.3 Build reproducibility | Fail (build fails) |
| 6.4 Native binaries | Fail (native.code unused) |
| 6.5 No reflection | Pass |
| 6.6 HTML documentation | Pass |
| 6.7 Tooltips & in-app help | Fail (no DocumentationExtension) |
| Manifest declarations | Fail (no plugin.editor_tabs) |
Overall: BLOCK. Note: no device verification was possible — the build never produced a .cgp.
| dependencies { | ||
| compileOnly(files("../libs/plugin-api.jar")) | ||
| compileOnly(files("../libs/eventbus-events.jar")) | ||
| compileOnly(files("../libs/shared.jar")) |
There was a problem hiding this comment.
Blocker (build fails). ../libs/shared.jar doesn't exist in the repo's libs/ (only common.jar, eventbus-events.jar, gradle-plugin.jar, idetooltips.jar, plugin-api.jar). assemblePlugin fails here:
> Failed to transform shared.jar to match attributes ...
> File/directory does not exist: .../libs/shared.jar
The jar providing com.itsaky.androidide.models.Range needs to be committed to libs/, or this dependency reworked to use what's already there.
| runCatching { editorService.hidePeerCursor(previous, peerId) } | ||
| } | ||
| runCatching { | ||
| editorService.showPeerCursor(local, line, column, peerId, peerName, peerColorArgb(colorIndex)) |
There was a problem hiding this comment.
Blocker (unreleased API). showPeerCursor / hidePeerCursor / clearPeerCursors are not present in the committed plugin-api.jar — they only exist on the feat/ADFA-4419-remote-peer-editor-decoration branch. Until that API lands in the baseline libs/, this repo can't compile the plugin from a clean checkout.
| return | ||
| } | ||
| PairLog.d("[PULL] calling openProject(${root.absolutePath}) exists=${root.exists()} isDir=${root.isDirectory}") | ||
| runCatching { service.openProject(root) } |
There was a problem hiding this comment.
Blocker (unreleased API). IdeProjectService.openProject(File) isn't in the committed plugin-api.jar (current surface exposes getCurrentProject() only). Same feat/ADFA-4419 dependency — the plugin can't build against the stage-tracked libs/.
|
|
||
| <meta-data | ||
| android:name="plugin.permissions" | ||
| android:value="filesystem.read,filesystem.write,network.access,native.code" /> |
There was a problem hiding this comment.
Blocker (rubric 6.4). native.code is requested but the plugin ships no native code — no .so, no jniLibs/, no externalNativeBuild, and no QR/camera library is declared in build.gradle.kts. (The HTML doc justifies it as "camera / QR-decoding native libraries", which don't exist here.) Remove native.code from plugin.permissions.
|
|
||
| <meta-data | ||
| android:name="plugin.sidebar_items" | ||
| android:value="1" /> |
There was a problem hiding this comment.
Blocker (manifest completeness). PairPlugin.getMainEditorTabs() contributes an editor tab, but there's no plugin.editor_tabs meta-data entry (only plugin.sidebar_items). Add a plugin.editor_tabs declaration — see icons-repository/src/main/AndroidManifest.xml for the pattern.
|
|
||
| <meta-data | ||
| android:name="plugin.min_ide_version" | ||
| android:value="1.0.0" /> |
There was a problem hiding this comment.
Conditional (rubric 6.1). Only plugin.min_ide_version is declared. The rubric wants a supported range — add plugin.max_ide_version so compatibility is bounded on both ends.
| import com.itsaky.androidide.plugins.extensions.UIExtension | ||
| import com.itsaky.androidide.plugins.services.IdeEditorTabService | ||
|
|
||
| class PairPlugin : IPlugin, UIExtension, EditorTabExtension { |
There was a problem hiding this comment.
Blocker (rubric 6.7 — in-app help). No class implements DocumentationExtension, so the plugin has no Tier-1/2 tooltips or Tier-3 offline help wired into the IDE. Needed:
- implement
DocumentationExtensionwithgetTooltipEntries()(non-emptysummary+detail) andgetTier3DocsAssetPath()backed by a realassets/HTML page; - add a
tooltipTagto the sidebarNavigationItemat line 59 (currently none — theEditorTabItem.tooltipstring alone doesn't cover the tooltip contract).
No description provided.