Skip to content

Catalog UI 4/5: one install action through the main-process API, with offline and error states (#18) - #35

Merged
cchacs merged 2 commits into
mainfrom
kilo/18-catalog-install-action
Oct 7, 2026
Merged

cchacs merged 2 commits into
mainfrom
kilo/18-catalog-install-action

Conversation

@cchacs

@cchacs cchacs commented Oct 7, 2026 •

Copy link
Copy Markdown

Part of #4. Closes #18 — Catalog UI 4/5. Accessibility pass follows in Catalog UI 5/5 (#19).

Changes

  • One install action through the main API. The install button sends the ticked selection to plugins.installCatalog and nothing else; main resolves (planCatalogInstall) and runs the batch. No new write path exists in the renderer — see the verification note below.
  • The final resolved installation set is visible before confirmation. The plan note under the checkboxes now shows the whole resolved set in install order (Install set (N): …), which entry pulled each dependency in (Required by the selection: Core (required by Practice Alpha).), what the conflict pass pruned (Will not install: …), the pinned transfer size (2 downloads (~466 KB)), and that installing needs a network connection. plugins:resolveCatalog now also returns downloadBytes (pinned sizes of the outstanding set).
  • Required dependencies are adopted and locked. A dependency something ticked requires is ticked + locked automatically (untick the dependent to release it), and its box can't be silently deselected while it's needed — the manager-side counterpart of the wizard's locked review rows. Entries the conflict pass pruned are unticked so a tick can't read as installed.
  • Offline is a first-class state. InstallError failures that a reconnect fixes (socket refused on fetch, stream interrupted mid-download) are typed networkRequired and propagated through BatchItemResult and the installFromCatalog answer. When every failure is that kind, the screen reports "A network connection is required to install plugins. Reconnect and try again — your selection is kept." The catalog section also states up front that installing downloads from the network while the catalog itself stays browsable offline (it reads from a bundled file).
  • A failed install is recoverable. The selection survives a failed run (minus what actually landed), so retrying is one click, and every failed plugin's message is listed instead of only the last one. Loading/progress/cancel states were already wired via onInstallProgress and are unchanged.
  • Incompatible entries can't be ticked for install. The install-list checkbox is disabled with the same actionable reason the browse card shows, and main refuses them (defense in depth) with that reason in the batch report.
  • Fixed a regression from Catalog UI 1/5: listCatalog() stopped attaching the lifecycle view (installed, pinned, disabled, updateStatus, updateAvailable, downgradeVersions), which left every Update/Pin/Downgrade/Enable/Uninstall control in the install list dead — only the checkboxes worked. Re-attached (with the canRollback: !disabled && hasBackup refinement from the pre-1/5 code).

Acceptance criteria addressed

  • The UI invokes only the main-process installation API; the renderer does not write plugin files. ✔ Verified by automated test and code review: a new static test asserts screen.js contains no require(/module/ipcRenderer/child_process/fs.*/process.exit|env|cwd, and that every plugins.<method>( call name in the screen is a method the preload bridge actually defines (installCatalog, resolveCatalog, …). Structurally the renderer runs with nodeIntegration: false, contextIsolation: true, and can only reach window.feedBackDesktop.*. The main process stays the only writer (installer staging + atomic swap, as documented in docs/PLUGIN_CATALOG.md).
  • Offline: browsing works; install reports the connection is required instead of failing silently. ✔ Browsing reads the bundled snapshot (unchanged); a connect-only failure is now classified and reported with a kept selection for retry; the plan note names the network requirement before the click.
  • A failed install shows a recoverable error. ✔ Per-plugin messages list every failure; the tick stays on the failed set; the button re-enables; retry re-resolves and re-installs only the outstanding ids.
  • Tests cover the new states. ✔ Added: offline install (message + kept selection), mixed install (landed pruned, failed kept, every message shown), success (clears + note closes), incompatible entry (locked out with reason), plan pricing before confirmation, resolver-lock mirroring, and the two installer classifications (offline vs HTTP 500 vs integrity).

Verification

  • npm run typecheck — clean.
  • npm test — 306 passed, 6 skipped; the only failure is the JUCE-submodule suite, which a clean checkout cannot populate (pre-existing).
  • New tests bite-verified by injecting the corresponding regression (offline classification removed, selection cleared on failure, lock removed, incompatible not disabled, networkRequired flag dropped, require() smuggled into the screen) — each made the relevant test fail.

…error states (#18)

- InstallError marks connectivity failures (networkRequired) so the UI can
  say 'a network connection is required' and keep the selection for retry,
  instead of presenting a broken install; BatchItemResult and the
  installFromCatalog answer propagate the flag.
- installFromCatalog refuses entries this build cannot run, with the same
  compatibilityFor() reason the cards show, before anything downloads.
- listCatalog re-attaches the lifecycle view (installed/pinned/disabled/
  updateStatus/updateAvailable/downgradeVersions) that Catalog UI 1/5 dropped,
  reviving the install list's Update/Pin/Disable controls.
- The install plan note shows the final resolved set (names, install order,
  transfer size, pruned conflicts, network requirement) before confirmation;
  required dependencies are adopted and locked; pruned entries untick.
- Failed installs keep the failed selection for a one-click retry and list
  every failure; a successful install clears the set.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Minor suggestions inline — nothing blocking.

Reviewed changes — Catalog UI 4/5: one install action through the main-process API, with offline and error states, plus a listCatalog() regression fix.

  • Install gate in main — installFromCatalog now resolves through planCatalogInstall(...).outstanding, refuses incompatible entries with the same compatibilityFor() verdict the cards show, and reports a top-level networkRequired when every failure is a connect-only one.
  • Installer classification — InstallError/BatchItemResult carry networkRequired for refused sockets and interrupted streams, but not for HTTP/integrity rejections.
  • Screen behavior — the plan note prices the resolved set and names what each required entry pulled in and what the conflict pass pruned; dependencies are auto-ticked and locked; failed runs keep the selection and list every failure; the screen still never writes plugin files itself.
  • listCatalog() regression fix — the lifecycle view (installed, pinned, disabled, updateStatus, downgradeVersions, …) is re-attached so the per-plugin controls in the install list work again.
  • Tests + docs — new offline/mixed/success/incompatible/pricing/bridge-only cases in both suites; docs/PLUGIN_CATALOG.md documents the new contract.

Verified locally: tsc --noEmit clean, and both touched suites plus the selection/compat/lifecycle/precedence suites pass (89 + 98 tests).

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using big-pickle (free) | 𝕏

Comment thread src/renderer/plugin-manager/screen.js Resolved
Comment thread src/renderer/plugin-manager/screen.js Resolved
@kilo-code-bot

kilo-code-bot Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (4 files)
  • src/main/plugin-installer.ts - comment clarity only
  • src/main/plugin-manager.ts - results typed as BatchItemResult[]
  • src/renderer/plugin-manager/screen.js - pruned-beats-locked mirroring + transitive adoption to a fixed point + resolver-error message
  • tests/plugin-catalog-view.test.js - two new regression tests
Previous Review Summary (commit 23eeb14)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 23eeb14)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
src/renderer/plugin-manager/screen.js 570 Resolver errors silently hide the dependency info without user feedback

SUGGESTION

File Line Issue
src/main/plugin-manager.ts 526 TypeScript type precision - results should be BatchItemResult[] instead of unknown[]
src/main/plugin-installer.ts 357 Size mismatch after download could indicate a network issue but doesn't set networkRequired: true
Files Reviewed (7 files)
  • docs/PLUGIN_CATALOG.md
  • src/main/plugin-installer.ts
  • src/main/plugin-lifecycle.ts
  • src/main/plugin-manager.ts
  • src/renderer/plugin-manager/screen.html
  • src/renderer/plugin-manager/screen.js
  • tests/plugin-catalog-view.test.js
  • tests/plugin-installer.test.js

Fix these issues in Kilo Cloud


Reviewed by free · Input: 591.3K · Output: 60.9K · Cached: 220.7K

success: boolean;
message: string;
/** The failure was a connectivity problem, so as a whole it is retryable. */
networkRequired?: boolean;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Consider adding JSDoc for networkRequired clarity

The networkRequired field on BatchItemResult indicates a connectivity problem that is retryable. The comment says "so as a whole it is retryable" but the field is per-item. Consider clarifying: "If true, this failure was a connectivity problem and the item is retryable."

Suggested change
networkRequired?: boolean;
/** If true, this failure was a connectivity problem and the item is retryable. */

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread src/main/plugin-manager.ts Outdated
export interface CatalogInstallResult {
success: boolean;
message: string;
results: unknown[];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: TypeScript type precision - results should be BatchItemResult[] instead of unknown[]

The CatalogInstallResult.results field is typed as unknown[] but the function returns BatchItemResult[]. Using the concrete type improves type safety and enables IDE autocomplete for callers.

Suggested change
results: unknown[];
results: BatchItemResult[];

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

}
catalogDependencyInfo.classList.add('hidden');
catalogDependencyInfo.textContent = '';
} catch (e) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Resolver errors silently hide the dependency info without user feedback

When plugins.resolveCatalog(ids) fails, the catch block hides catalogDependencyInfo but shows no error message. The user sees the plan note disappear with no indication of why. At minimum, log the error to console for debugging; ideally, show a transient message like "Could not resolve selection — try again."


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

…on, resolver error surfaced

- mirrorPlanOntoRows: a resolver-pruned entry is unticked before any lock is
  applied, and pruned dependents no longer adopt or lock their own
  dependencies behind them; the 'Required by the selection' line never names
  a pruned entry.
- Adoption iterates to a fixed point, so a transitive chain (A requires B
  requires C) lands on the rows in the same resolve instead of one per round
  trip.
- A resolveCatalog failure now says 'Could not resolve the selection. Try
  again before installing.' instead of silently hiding the note.
- CatalogInstallResult.results typed as BatchItemResult[]; per-item
  networkRequired JSDoc clarified.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — Incremental review since last Pullfrog review (23eeb14..f55d5ac).

  • Resolver conflicts take precedence over locks. mirrorPlanOntoRows now computes prunedIds from the plan's conflicts and removes pruned entries from the selection before lock computation; lockedBy checks only apply to non-pruned entries so a dropped entry is never adopted or locked behind its dependent.
  • Transitive dependencies are adopted in one pass. A fixed-point loop re-runs planLocks as newly adopted dependencies bring in their own deps, so the full chain appears checked and locked immediately and the plan note accurately describes who requires what.
  • Plan note construction is cleaner. The note now groups dropped entries under "Will not install" using named entries, filters pruned IDs out of the locked list, and surfaces a concrete resolver failure message if the plan cannot be read.

The tests added in the incremental commit verify the pruned-beats-locked behavior, transitive adoption, and the note text; no behavior changes outside the renderer's plan mirroring.

Pullfrog  | View workflow run | Using big-pickle (free) | 𝕏

@cchacs
cchacs merged commit 589ad86 into main Oct 7, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Catalog UI 4/5: one install action through the main-process API, with offline and error states

1 participant