feat(portable): record third-party clients in the pinned capability table - #721
Conversation
🦋 Changeset detectedLatest commit: d3c5362 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4d86159843
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
90f95de to
3d43588
Compare
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
Owner-requested review — hold for compatibility/installation corrections
Reviewed 3d435889a47acf3d9a1443cab6535c6c9f00d7c6 through GitHub: changed source, capability records, generated-install/documentation paths, tests and existing review threads. No local build or native-host execution was performed by this reviewer. GitHub currently reports conflicts with main.
The direction is appropriate: document readers of the existing portable artifact using the existing capability table, rather than creating another catalog or one runtime per host. This PR is a documentation/compatibility-record slice, not implementation of all 21 native host integrations.
Required corrections — existing P2 threads are valid
I checked the five open inline findings against this head; they are not stale:
- Use the actual install action, not array position.
src/install/surface.ts::clientLinerendersrecord.install.commands[0]afterInstall:. Qoder's first element isqoder plugins validate ...; the install command is second. Give actions a small explicit role or render a correctly labelled sequence. Reordering one record alone leaves the same failure mode for the next client. - Do not assert that optional components exist in this artifact.
portableInstructions()renders static records without the emitted plan.portable.tsexplicitly marksmcp.jsonoptional, and a valid portable selection need not emit portable skills. Distinguish mandatory format markers from optional component discovery paths; do not treatplugin.json + skills + mcp.jsonas a universal all-required bundle shape. Either qualify these as generic conditional capabilities or use the existing planned/file inventory when saying "this bundle"—not another filesystem discovery pass. - Do not publish the unproved Grok local-install recipe as established setup. The record itself says the consulted examples only establish marketplace-name installation, yet
Install:presents<source>for the emitted directory. Omit it or clearly identify its verified source kind. Keep explicit trust approval separate from a generic copy-paste installation recipe. - Fix the homepage's native-reader claim in both locales. A matrix with
agent-plugins,skills, andnonecannot describe all its entries as native readers of this format. Name compatible, partial, and incompatible/undetermined cases accurately. - Separate whole-manifest precedence from per-component overrides.
clientLinesays anyshadowedBypath makes the whole root "read as that instead". A dotted MCP file can replace the MCP source while shared skills still load. Preserve which surface is affected; do not turn a component override into total format replacement.
Additional guidance grounded in these same paths
clientLine also omits the reason attached to degraded rows. Keep those limitations visible where installation guidance is printed. For Qoder, recognizing the mcp.json file is not evidence that generated ${PLUGIN_ROOT}/${PLUGIN_DATA} launch values execute correctly when the same record marks placeholder behavior unavailable/unproved. Distinguish document recognition from working executable launch, and documentation-derived evidence from a native smoke test. This does not require blocking this PR on every future host implementation.
The fixes can stay small: correct the record semantics and render them faithfully. No universal discovery interpreter, extra projection registry, or duplicated host runtime is needed. The five records here should not close #693–#714 collectively; native codecs, SDK entry points, install ownership, and conformance remain the corresponding host issue's work.
Regression matrix before merge
- Empty/content-free portable fixture, skills-only, MCP-only, and both components: no fabricated present surfaces and no false mandatory-component claim.
- Portable plus Claude/native marker: whole-format selection distinguished from MCP-only shadowing.
- Qoder output includes the real install command; validation remains validation.
- Unverified source kinds and degraded placeholder/launch caveats remain visible in INSTALL and generated en/zh docs.
- Actual rendered strings must be asserted, not only the presence of the client name or the old misleading sentence.
Resolve the current conflicts, disposition all five inline threads, and record the repository's required local gates for the updated head. The PR workflows retrieved for this reviewed SHA were still pending/queued; this review does not claim they passed.
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
Follow-up on 956f0f6 — preserve the scope reduction and finish the evidence-to-prose fixes
Owner-requested follow-up. I checked the current capability records, src/install/surface.ts, tests/install-surface.test.ts, and website/plugins/generated-reference.ts through GitHub. No build, native-host run, or local test was executed by this reviewer. This is a delta to my earlier review, not a request to redo completed fixes.
Scope decision: Aider and Jules are excluded
The owner has removed Jules (#712) and Aider (#713) from the requirements. Their issue comments explicitly say no implementation or compatibility claim should be added. Replace the PR body's current statement about 21 open host-support issues with the reduced requested scope (19 originally requested integrations after these two removals), or avoid an easily stale count and link only the applicable issues. Do not add Aider/Jules capability rows, adapter work, installation instructions, or acceptance obligations while expanding this table. Historical research can remain historical; it is not an active requirement.
P2: unproven compatibility still renders as a proven rejection
portable-1.0.0.json now correctly says Antigravity manifest handling and subsequent skill discovery are unproven, with no CLI run establishing whether the emitted tree is accepted. But src/install/surface.ts::clientTierSentence() still renders tier none as:
loads nothing from this bundle as published
The generated reference's clientsIntro makes the same unconditional claim. tests/install-surface.test.ts still asserts that text and comments that the host rejects the manifest. Thus the record was corrected but the user-visible conclusion was not.
Render this as no compatibility established by this record, and preserve the dated reason. Distinguish an unverified surface from a demonstrated unsupported format; a conservative support policy is appropriate, but it must not masquerade as observed host behavior. This can be a wording/data-rendering correction within the existing table, not a new compatibility subsystem.
Regression: use the present Antigravity record and assert the generated INSTALL and both reference locales do not claim a tested rejection or that component discovery was observed to fail. Update the test's explanation as well as its expected string.
P2: the abbreviated INSTALL line still drops degraded conditions
clientLine() prints reasons for neither degraded rows nor unavailable/unproven rows; its withheld list only selects unavailable/prohibited surface names. The populated Qoder test currently locks in:
loads its skill tree only. Reads:
mcp.json,skills.
That is internally confusing, and it hides the actual restrictions already present in the record: .mcp.json takes precedence over mcp.json, and expansion of the generated ${PLUGIN_ROOT}/${PLUGIN_DATA} values is not established. Merely listing a config file as read is not a claim that the generated local launch will work.
Keep the recorded qualifications in the installation-facing output, or link each abbreviated claim directly to a rendered per-client explanation. Separate the skills support level, MCP-document recognition, and launch compatibility rather than describing them all with only. A portable-plus-Claude fixture should keep the MCP precedence caveat visible, without claiming the entire plugin is replaced by a component override. Check the actual rendered text; the current test deliberately expects the conflicting sentence.
Earlier findings that should not be replayed as unchanged
The Qoder record now puts installation before validation, empty command lists are rejected, the reference consumes the shared validator, whole-manifest shadowing no longer lists .mcp.json, and component-empty fixtures now report absent read paths. Those are real improvements. I have not run the newly documented Grok command, so its documented source evidence must remain distinct from native execution proof.
The remaining corrections belong in this PR and the existing host issues; no duplicate issue is needed. This capability-record slice still does not deliver native hooks/SDK entry points/install ownership for every researched host. Preserve the existing implementation boundaries and the owner's explicit exclusion of #712/#713.
…able Add an optional `clients` block to the portable capability table: per client, the artifact paths it reads, the manifests that shadow them, its verbatim install commands, and a dated row per surface it does and does not load. One validator over the existing records holds a tier to its rows, and INSTALL.md plus the generated en/zh host reference print those records instead of an unsourced list of native clients.
956f0f6 to
e94d6c5
Compare
Why
INSTALL.mdand two documentation pages asserted a bare list of Agent Plugins"native clients" (Codex, VS Code, GitHub Copilot, Kiro, ChatGPT) with no source,
no per-client install command, and no statement of what each client actually
loads. The 21 open host-support issues (#693–#714) mostly ask the same question:
what does an existing artifact really give a given agent? That is evidence about
a reader of the artifact, not a new projection — so it belongs in the pinned
capability table the adapters already compile against, not in a second catalog.
What
packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.jsongains anoptional
clientsblock. Each record pins one client to its owndocumentation:
observed(version or doc date),tier,discovery.requiredand
discovery.shadowedBy, verbatiminstall.commands, and a row per surface(
manifest,skills,mcp,placeholders,hooks).clientCompatibilityFrominadapters/capability-state.tsis the singlevalidator over those records, beside the notice-delivery and event-route
readers that already live there. It reuses the table's dated-evidence rule:
supportedneeds dated evidence, anything less needs a dated reason, a silentsurface is a build failure, and a tier is held to its rows (
agent-pluginsrequires a manifest the client loads outright,
skillsrequires the skilltree,
nonemay claim no loaded surface at all).install/surface.tsprints one line per record — what it loads, its installcommand, what it does not load, and what would shadow it — replacing the
unsourced sentence.
website/plugins/generated-reference.tsrenders the samerecords into the generated en/zh host matrix.
(Host support: Devin CLI — native plugin manifest, portable compatibility, and versioned MCP/hook profiles #701) and OpenClaw (Host support: OpenClaw — compatible bundles first, native plugin and ClawHub skill tiers separately #702) at
agent-plugins, Grok Build (Host support: Grok Build — plugin discovery, native hook semantics, and MCP configuration #700) and Qoder CLI(Host support: Qoder — native plugin projection with explicit CLI and IDE capability profiles #696) at
skills, Antigravity (Host support: Antigravity — native plugin projection, Gemini migration boundaries, and explicit CLI/IDE contracts #714) atnone.Every unsupported surface is an explicit dated row, never a silent omission:
hooks are
unavailablefor all five because Agent Plugins 1.0.0 defines nohooks and this projection emits no hooks document, and each reason names the
file that client would have needed instead.
Gate
Run on
host-compat-mechanismrebased onto currentorigin/main(6bce18953d),at tip
fba03f7cb9:pnpm build— passpnpm typecheck— passpnpm lint— pass (1479 files, 88 rules)pnpm test:unit— 0 failed, 4427 passedpnpm docs:site:build— pass: locale drift 0 failures across 36 page pairs,0 broken links / 28329 anchors checked
Deslop
Deslop: claude-opus-5-thinking-high, 4 edits — unexported two internal
constants, removed the duplicate
readInstallcall from the first draft,dropped the separate
host-compatibility.tscatalog in favor of the existingtable, and reworded two adjacent install sentences that leaned on the deleted
claim.
Self-review
Reviewer:
gpt-5.6-sol-max(change-risk-reviewer), two passes. Ten findings, alladdressed in
956f0f6d56:skills/mcp.json— fixed:INSTALL.mdnow prints, per client, only the discovery paths the builtbundle carries, and says so when it carries none. Both cases are tested.
commandsarray renderedInstall: undefined— fixed: the installcommand is first in the record, and an empty list fails the table.
skillsclient does not read the manifest, which itsown
degradedmanifest row contradicted — fixed: the line reads "loadsits skill tree only", and a tier is now held to its paths as well as its
rows (
agent-pluginsnamesplugin.json,skillsnamesskills,nonenames nothing).
shadowedByrendered a per-surface narrowing as whole-plugin precedence —fixed: it is manifest precedence only; the
.mcp.jsonnarrowing lives ineach
mcprow, and Qoder's unproven manifest-precedence row is gone.degradedsurface could carry no evidence, and paths could be named withno discovery evidence — fixed: both are now build failures, with tests.
isRelocatablePosixPath, witha backslash-path rejection test.
fixed: it renders
clientCompatibilityFromrecords, so an invalid recordfails
pnpm docs:site:build. Partially dismissed: no dedicated rendererunit test was added — the docs build runs the validator on every record and
validator rejection is unit-tested.
README.md,docs/framework-mode.md, and bothindex.mdxlocales kept theunsourced native-client list — fixed in all four.
revision documents
./local-dir— fixed: the verbatim quote and a localinstall command.
nonetier asserted a refusal its citation does not state —fixed: the manifest and skills rows are recorded as explicitly unproven.
Review threads (Codex P2, all five resolved in
e94d6c56a2)install.actionsis now{ role, command }with rolesinstall,trust,enable,verify,inspect,remove; the validatorrequires exactly one
installaction andINSTALL.mdprints that one, soQoder's
validatecan never be published as its install command.installSurfaceEntriesreceives the paths the hostprojections actually planned; each client line names only its recorded paths
that the build wrote, and says "This bundle emits none of the paths it reads"
otherwise. A table-driven test renders all four inventories (no component,
skills only, MCP only, both).
source: marketplacewithgrok plugin install <marketplace plugin name>; the unproven local recipe isgone (asserted absent), and trust is its own action and its own evidence line.
incompatible clients; the unsourced list is also gone from
README.mdanddocs/framework-mode.md.shadowedByis{ path, surfaces }; only a shadowthat takes
manifestrenders as replacing the plugin, and a per-surfaceshadow renders as "uses it for mcp and still reads the rest".
Also in that commit:
degraded.reasonis printed as a sub-bullet per narrowedsurface, and both the install prose and the generated reference state that
reading
mcp.jsonand expanding the reserved placeholders are separate rows.Follow-up in
fba03f7cb9: shadowing files keep their authored order, because aclient that publishes a precedence order among those files publishes it in that
order (Devin:
.devin-plugin/plugin.json>.claude-plugin/plugin.json> root).Self-review round 3 (
claude-fable-5-1-thinking-highandgpt-5.6-sol-max, tipd3c5362672)Both reviewers read the diff against
origin/main. Findings and disposition:clientLinesplits recordedprecedence files into the ones this build wrote and the ones it did not. An
emitted shadow drops the surfaces it takes from
Reads:and renders as fact("This build also writes
.mcp.json, which it uses for mcp instead."); a filethat takes every read surface renders as "this build also writes
.claude-plugin/plugin.json, which it reads as the plugin instead" with noinstall command. A new test composes
claude+portableand asserts exactlythat for Devin, OpenClaw, and Qoder.
sentence is now "Install (no local-directory install is verified for this
artifact)", mirrored in both
clientsIntrostrings.skillstier said "skill tree only" while Qoder also readsmcp.json(both). Fixed: the tier renders as "loads the components it recognizes
without reading the manifest", in the prose and both locales.
--trust(Sol). Fixed: theinstallactionis the documented spec verbatim (
grok plugin install <name> --trust), and theseparate
trustaction is/hooks-trust, the in-session command installcannot express.
manifestwasdegradedon the basis Antigravity's isunavailable(Fable). Fixed:
unavailable, same "unproven" reason. Tier unaffected..mcp.jsonwas modeled as a shadow though both documents are readand merged (both). Fixed: the shadow is gone;
mcpisdegradedwith thecollision rule quoted, so the limit renders where it applies.
mcpand OpenClawskillswere degraded for conditions thisartifact cannot hit (Sol). Fixed: both are
supported; Qoder's precedencelives in its
.mcp.jsonshadow, and OpenClaw's nested-tree limit cannot reachan artifact whose skill roots are always immediate children of
skills/.validator rule binds
manifest/skills/mcptoplugin.json/skills/mcp.jsonthrough the exportedCLIENT_SURFACE_PATHS, which the renderer thenuses to decide which surfaces a build actually wrote.
Fixed: both are gated on the client reading something this build wrote — a
componentless bundle now says "there is nothing to install there".
lifecycle.uninstall.reason.local-directoryinstall at tier
none, non-listshadowedBy, unknown surface in a shadow,a loaded surface missing its path, and the shadow-order case.
fixed in
fba03f7cb9; authored order is kept.website/has no test pool, andpnpm docs:site:buildfails on an invalidrecord because it renders through the same validator. The rendered en and zh
tables were read after the build and carry the roles, per-surface shadows, and
degraded reasons.
paragraph routinely covers several paths at once (a discovery section listing
skills/andmcp.jsontogether); splitting quotes per path would fragmentthe evidence without making it stronger. The rule stays "at least one dated
note whenever any path is named".