refactor(tui): re-land FEAT-020 plugin command shapes on main - #5865
Conversation
…es to command contract - CommandPluginContext: object-safe synchronous facet covering registry reads/mutations, async-bridged install/update/uninstall with sync receipts (D11), export, legacy scan, kimi managed import, and marketplace - Portable DTOs: PluginSummary/Detail/Diagnostic/McpServerDetail, mutation outcome+receipt, export receipt, legacy tool+scan, managed candidate+scan, marketplace catalog/candidate/add/state, suggestion - PLUGIN = 1 << 10 capability bit and one plugin envelope slot with with_plugin builder - Contract tests: object safety, field/variant closure, sync receipt outcomes, exact-hash mismatch, managed/marketplace portability, envelope transport, duplicate-slot rejection, bit stability - Contract boundary gate green; workspace compiles; fmt clean; 23/23 contract tests pass Generated with Claude Code Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…misleading receipts Code-review finding: trust/enable/disable/revoke_trust returned a PluginMutationReceipt with outcome always NoChange, which is semantically wrong (NoChange means 'already up to date' in the install/update path). The host registry returns Result<(), String>; the handler renders the action word from its own dispatch arm and re-reads detail for post-mutation state. Return Result<(), String> — the exact-minimum typed surface. Generated with Claude Code Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…tricted exposure - PluginAdapter implements CommandPluginContext against App: registry reads (summaries/detail/diagnostics/validation/suggest), registry mutations (trust/enable/disable/revoke with skill-cache + active-skill side effects), async-bridged install/update/uninstall with synchronous receipts (D11), export, legacy scan, kimi managed scan/install, marketplace state/add/remove/install (incl. builtin official catalog) - CommandContextBundle grows to eleven slots with plugin; contexts() exposes plugin only for PLUGIN capability - Portable conversion helpers: summary/detail/mcp server/diagnostic/marketplace diagnostic/mutation receipt/export receipt/legacy tool/marketplace candidate/catalog - kimi_import: scan_managed_plugins_portable wrapper; group modules made pub(crate); plugin_network_policy/run_async exposed - Adapter tests: host-data projection, registry mutation + suggest behavior, restricted exposure (3 tests) - Full TUI lib suite 11395/0; boundary gate green; fmt clean Generated with Claude Code Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…acet parity - mod.rs: portable plugins() dispatch consuming workspace/presentation/plugin facets; legacy shell builds bundle and delegates (Phase 6 replaces with from_contract) - render.rs: render_bundle_detail/escape helpers consume portable PluginDetail + presentation facet - legacy.rs: consumes PluginLegacyScan; kimi_import.rs: consumes PluginManagedScan; marketplace.rs: consumes PluginMarketplaceState with localized plan text - Presentation facet: key_to_plugin_message_id maps all 52 plugin keys; source_path carried for marketplace provenance - Contract: PluginSuggestion.state_label, PluginDetail.inventory_summary, PluginMarketplaceCatalog.source_path, reload() facet method - Tests: 18 plugin tests converted to the portable shell path; full parity preserved - Full TUI lib 11394/0; contract 23/23; boundary gates green Generated with Claude Code Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…nk both frontiers - PluginsCmd implements contract RegisterCommand<CommandResult> with exact WORKSPACE | PRESENTATION | PLUGIN; PluginsCommands group registers via ContextualCommand::from_contract - plugins_contextual destructures facets with safe missing-facet errors; transitional App shell now test-only - Public dispatch tests: exact capability set, undeclared facets absent, public seam dispatch, no-panic matrix (3 tests) - Remove plugins from PENDING_GROUPS and scripts/command-migration-topology.json frontier (same commit) - Migration fixture updated for six-group frontier; feat015 legacy-assertion test adds plugin to MIGRATED - All gates green: contract 23/23, TUI lib 11397/0, migration/boundary/CI fixtures + live gates, fmt, diff hygiene Generated with Claude Code Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
- Fix clippy findings in FEAT-020 plugin files: identical if blocks (contract.rs), useless as_ref/map (marketplace.rs), useless format + redundant closure (render.rs), manual unwrap_or_default (mod.rs), collapsible if (contract tests) - Boy Scout: repair pre-existing lints outside FEAT-020 scope (computer-use linux.rs &PathBuf->&Path, config catalog tests contains()/type_complexity) - cargo clippy --workspace --all-targets --locked -- -D warnings exits 0 with zero warnings Generated with Claude Code Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
rollback_hash_mismatch called crate::plugins::install::uninstall directly from the portable handler, a TUI-owned executable dependency that violates the D1 boundary and would break the FEAT-040 physical move. Add CommandPluginContext::uninstall_path(name, plugins_dir) - a file-level rollback removal with no registry resolution or skill side effects - and route the content-hash-mismatch rollback through it. The host adapter owns the crate::plugins call. Verified: contract 23/23, plugins group 18/18, plugin-scoped TUI suite 205/0, clippy -D warnings clean. Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
Append PLUGIN after the capability identities already published by FEAT-021/022 and preserve current-main behavior for marketplace-backed suggestions, one-shot reload nudges, canonical review tokens, and shared marketplace document validation. Do not resurrect the retired computer-use builtin catalog. Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
Scope symlink refusal tests to Unix, where symlinks are actually created, so Windows all-feature test compilation remains warning-free. Scan all visible trust-command candidates in the binary acceptance harness so a retained partial command cannot hide the complete canonical review token. Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
|
I checked both failed jobs and pushed focused repairs in
Local post-fix checks pass:
A fresh full matrix is now running. The PR remains Draft. Paulo Aboim Pinto |
Replace the stale 0.0.0.0 warning expectation with an explicit non-loopback rejection check and verify that the error explains the enforced loopback-only boundary. Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
|
The new matrix fixed the previous Ubuntu and Windows failures: both full test jobs are green. The remaining Mobile runtime smoke failure was another stale current-main expectation: the script still expected an insecure mobile server to default to I pushed
The complete smoke script now passes locally, 9/9, against the current debug binary. Shell syntax and diff hygiene also pass. A fresh matrix is starting, and the PR remains Draft. Paulo Aboim Pinto |
|
The fresh head matrix for Passed checks include Linux, macOS, Windows, lint, Safety gate, Mobile runtime smoke, Runtime integrations, OHOS cargo check, npm wrapper smoke, signed-off-by, link, version drift, and GitGuardian. The remaining skipped jobs are intentional workflow outcomes. The previously failing jobs are confirmed repaired:
The branch is clean and GitHub reports it mergeable. I have marked PR #5865 Ready for Review. Paulo Aboim Pinto |
Re-lands the plugin group on the portable command-contract spine: a plugin capability and facet with typed outcomes, a TUI adapter with full host mapping and restricted exposure, registry mutations returning Result<(), String> instead of misleading receipts, and `/plugin` registered through the portable bridge. Ten commits, all authored by Paulo Aboim Pinto and preserved as his. Merged rather than rebased: the branch went stale because main moved under it, not because of anything the contributor did, so the maintainer resolves. Four conflicts, resolved by taking one side whole rather than splicing: - `plugins/marketplace/document.rs` — theirs. Gating the whole symlink test on `#[cfg(unix)]` beats main's per-statement `cfg` attributes, whose non-unix branch called `load_catalog_document` and discarded the result. - `scripts/mobile-smoke.sh` — theirs. Main updated the old shape (start a server, grep stdout for a warning); theirs asserts the boundary is actually enforced — that `serve --host 0.0.0.0 --mobile` exits non-zero and explains why. Verified against `validate_runtime_listener_security`, which does bail with "Codewhale mobile is loopback-only ...", and their Mobile runtime smoke check passed. - `cucumber/plugin_e2e_acceptance.rs` — theirs. `find_map` with `?` scans every marker exactly as main's `for` loop did; semantically identical, more direct. - `commands/groups/plugins/tests.rs` — genuinely both. Main added the built-in computer-use bundle (so the list is 2 bundles, not 1); theirs changed the call to `plugins_with_kimi_home_override`. Kept their call shape and re-applied main's assertions at the same anchor. Gates: fmt CLEAN · clippy -p codewhale-tui PASS · nextest -p codewhale-tui 11997 passed / 0 failed / 13 skipped · check-command-crate-boundaries, check-command-migration-manifest, check-dead-code-budget, check-runtime-contract-budget and the manifest self-test all PASS. Harvested-from: PR Hmbown#5865 by @aboimpinto Co-authored-by: Paulo Aboim Pinto <1231687+aboimpinto@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QCLLenseqfQdVaZUBqn9yG
0.9.12 had no `### Contributors` section, `web/lib/release-credits.ts` still held the 0.9.11 cohort, and the contributor doc band was missing five people whose work landed. Three surfaces that must agree, disagreeing three ways. Everyone whose work reached main this cycle is now named in all three, derived from `Co-authored-by` and `Harvested-from` trailers on v0.9.11..HEAD rather than from memory. The five who were missing: - @qiuYliangM (秋月凉梦) — co-author on Hmbown#5621, Hmbown#5622, Hmbown#5623 - @whp233 — Hmbown#5716, landed as Hmbown#5719 - @Gabriel-Degret — Hmbown#5867 - @huangxianzhan — Hmbown#5868 - @zhuowp — Hmbown#5869 @aboimpinto's entry gained Hmbown#5825 and Hmbown#5865, @M-Maciej's gained Hmbown#5533/Hmbown#5831. `requiredCandidateCredits` in public-surface-facts.json moves from the 0.9.11 cohort to this one — that guard is meant to hold the *current* release's credits in every surface, and it had been pinning the previous release's. `RELEASE_HELPERS` is deliberately empty. Every credited contribution this cycle arrived as code that landed; a name there that no landed change can point at would be a nicer-looking ledger and a less true one. AUTHOR_MAP gains canonical numeric-noreply identities for all five, since the trailers on their harvest commits used raw emails and old-style noreply addresses. web: 386 tests, 386 passed. check:facts, check:docs, check:tokens, check:locales all PASS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QCLLenseqfQdVaZUBqn9yG
No-Issue: FEAT-020 is tracked in umbrella #5316, which must remain open for the remaining command-decomposition work.
Summary
Re-lands FEAT-020 on current
main. The original implementation PR #5657 was merged intocodex/v0912-integration-20260823, notmain; neither its merge commitece99d5nor its feature head is an ancestor of current main.This forward-port preserves the original seven authored commits, adds one current-main reconciliation commit, and includes one focused Boy Scout test repair for failures reproduced on current
main. It:CommandPluginContextfacet and typed portable plugin/marketplace outcomes;PluginAdapter, keepingApp, registry, config, async/network, marketplace, Kimi, and legacy-tool machinery host-side;/plugintoContextualCommand::from_contractwith exactWORKSPACE | PRESENTATION | PLUGINauthority;Command capability unavailable: <facet>;pluginsfrom both migration-frontier representations, leaving[config, core, debug, session];/pluginnames, aliases, usage, rendering, trust/install/update/uninstall behavior, marketplace CRUD, and Kimi import behavior.Current-main reconciliation
This is intentionally not a blind replay of the old integration branch:
PLUGINis appended at1 << 12; current main has already publishedPROJECT = 1 << 10andSKILL_GROUP = 1 << 11through FEAT-021/022./plugin list.LoadedPlugin::review_tokenimplementation.Current-main CI blocker repairs
-D warningsfailure without weakening Unix coverage.0.0.0.0warning smoke check with explicit rejection and loopback-guidance assertions, matching the runtime's enforced security boundary.Validation
cargo fmt --all -- --check— PASScargo test -p codewhale-command-contract --lib --locked— 43/43 PASSpython3 scripts/test_check_command_crate_boundaries.py— 8/8 PASS; live boundary gate PASSpython3 scripts/test_check_command_migration_manifest.py— 56/56 PASS; live migration gate PASS-D warnings— PASSgit diff --check— PASSA single-process full TUI lib run exercised all tests: 11,536 passed; tests competing for the process-global runtime owner lock failed together, while every plugin/marketplace Runtime API failure relevant to this change passed independently with an isolated runtime directory. The authoritative CI uses one-process-per-test
cargo nextestand will remain the merge gate. This PR stays draft until the complete required matrix is green.Provenance
ece99d5aba8288024e9aac08e8e29c13ac2209bfPaulo Aboim Pinto