Skip to content

refactor(skills): remove the unreachable Skills page and the file count it rendered - #14259

Merged
nwparker merged 4 commits into
mainfrom
nwparker/remove-orphaned-skills-page
Aug 13, 2026
Merged

refactor(skills): remove the unreachable Skills page and the file count it rendered#14259
nwparker merged 4 commits into
mainfrom
nwparker/remove-orphaned-skills-page

Conversation

@nwparker

@nwparker nwparker commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

ELI5

There's a "Skills" page in the app that nobody can open — the button that used to open it was deleted ten weeks ago and the page was left behind. This removes the page, and the expensive number it was the only thing displaying.

What Changed

Two removals, one commit each, each independently valid.

1. The unreachable Skills page. activeView === 'skills' has had no way to be set since #4535 removed the sidebar toolbox menu on 2026-06-02 — that commit deleted the only onSelect={openSkillsPage} and left everything behind it in place. There is no button, command-palette entry, menu item, keybinding, deep link, or e2e spec that reaches it.

Removed: SkillsPage, SkillCard, skills-filter, skill-display-labels, the openSkillsPage / closeSkillsPage actions, previousViewBeforeSkills, the 'skills' member of TopLevelView (and its lookup, RPC schema, and right-sidebar entry), and 26 localization keys used only here.

Untouched: every skill freshness component in the same directory (SkillFreshnessNudge, SkillFreshnessUpdateDialog, SkillUpdateRow, the status pill, the run store). Those are live from App.tsx, the status bar, and Settings — I classified all 22 files in components/skills/ before deleting six.

2. DiscoveredSkill.fileCount. It existed solely to render "N files" on the card removed above. Producing it cost a second full walk of every skill package on the native path, and one find subprocess per discovered skill inside the WSL scan script.

Why

countPackageFiles was pure waste the moment its only consumer became unreachable — a recursive walk per package, on the Electron main process's libuv threadpool, for a string nothing rendered.

Measured on the same 32-scan / 8-workspace repro used in #14204:

filesystem calls note
main 2,956
this branch 2,804 -5.1%

I want to flag that honestly: I previously estimated this at "roughly a third" and that was wrong. The native saving is small because the walk was already depth- and node_modules-bounded by #14204, so the second walk was cheap. The real win is WSL, where the per-skill find loop disappears: a 14-root scan of 31 skills drops from 45 find invocations to 14 (-69%), and the WSL protocol record goes from 6 fields to 5.

Linked Issue

No tracked issue — found while tracing where fileCount was rendered during #14204 review.

Fixes #

Visual Proof

N/A — nothing rendered changes, because nothing rendered this. The page had no route into it and the "N files" label was inside it.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Two existing tests caught the change and were updated rather than deleted, which is the evidence it does what it says:

  • skill-discovery-concurrency.test.tsREADDIR_CALLS_PER_POPULATED_ROOT 3 → 2, because the second package walk is gone.
  • skill-discovery-wsl.test.ts / skill-discovery-wsl-plugins.test.ts — the WSL wire fixture is positional, so dropping the file_count field required removing it from each S record.

Also removed the countPackageFiles test block, and fileCount from 16 DiscoveredSkill fixtures.

Reviewed against the full readiness checklist across two rounds (3 agents, then 1). Neither round found a defect. Round one raised three gaps, closed in 98c7fa6f4b3:

  • The WSL scan protocol had no structural guard. It is a positional NUL-delimited protocol whose producer is a bash script and whose consumer is a parser in the same file; every existing test hand-encodes the record, so both sides can drift together with a bug — this PR's own 6→5 field change was verified only by editing fixtures to match. Added a test that runs the real generated script and feeds its real stdout to the real parser. Mutation-verified: re-adding a field to the script fails it.
  • Four stale locale-override entries keyed on i18n keys this PR deleted. Nothing validates that override keys exist, which is why lint stayed green — pruned.
  • The 'skills''terminal' hydration demotion is now pinned by name, so the removal reads as a deliberate migration.

pnpm lint (incl. all four localization gates), pnpm typecheck, and the full suite pass. Verified commit 1 typechecks standalone. Two failures in src/relay/agent-exec-handler.test.ts are pre-existing — confirmed failing on clean main — and one github-project test is flaky under full-suite parallelism (passes in isolation, references none of these surfaces).

AI Disclosure

Claude Opus 4.5 via Claude Code.

Review

  • Backwards compatibility (the one real risk). Removing fileCount is a Rule 3 change per docs/reference/remote-wire-compatibility.md"a field the host stops populating (an old client reading it now sees undefined)". It is unobservable by any shipped client, verified from history rather than assumed:

    So the label was added to a component that had already been unreachable for two months. No build has ever rendered it, which means no paired old client can observe the field going missing. Note also that runtime-skills-client.ts:31 casts the RPC result without zod validation, so there is no schema rejection path either — the field simply becomes undefined on a surface nothing can reach. The cross-version harness covers the terminal stream only, so it would not catch this either way; this reasoning is the artifact.

  • Dropping 'skills' from TopLevelViewSchema is safe by existing design: UiUpdate wraps every field in tolerateUnknownValues (.catch(() => undefined)), so an old client sending activeView: 'skills' has just that value dropped and the rest of the batch still lands — which is precisely what that helper was built for.

  • Persisted state needs no migration: sanitizeHydratedActiveView already falls back to 'terminal' for anything failing isTopLevelView.

  • Security / cross-platform / mobile — no new surface; WSL script emits one fewer field and no per-skill subprocess; mobile has no skills consumer.

Note for the reviewer

This deletes the only UI for browsing installed skills. The native-chat @-picker still lists them inline and Settings keeps its per-feature install prompts, but there is no longer a "here is everything installed and where it came from" view. It has been unreachable for ten weeks, and restoring it is a git revert — but if the intent was to re-link it rather than drop it, this is the PR to say so on.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • pnpm lint, pnpm typecheck, pnpm test pass (pre-existing unrelated failures noted)

Made with Orca 🐋

`activeView === 'skills'` has had no way to be set since #4535 removed the
sidebar toolbox menu on 2026-06-02. That commit deleted the only
`onSelect={openSkillsPage}` and left the page, its store actions, and its
view member behind. Every release tag in the current window postdates it,
so no shipped build can reach this surface.

Removes the page, SkillCard, its filter and label helpers, the
open/closeSkillsPage actions and `previousViewBeforeSkills`, the 'skills'
member of TopLevelView, and 26 localization keys used only here.

Dropping 'skills' from TopLevelViewSchema is safe for a paired old client:
UiUpdate wraps every field in `tolerateUnknownValues`, so a value the
schema cannot express is dropped from the payload and the rest of the
batch still lands. Persisted state is already covered —
`sanitizeHydratedActiveView` falls back to 'terminal' for any value
failing `isTopLevelView`.

The skill freshness components in the same directory are untouched; they
are reached from App.tsx, the status bar, and Settings.
`DiscoveredSkill.fileCount` existed to render "N files" on a skill card,
and that card was the surface removed in the previous commit. Producing it
cost a second full walk of every skill package on the native path, and one
`find` subprocess per discovered skill inside the WSL scan script.

Native, on the 32-scan / 8-workspace repro: 2,956 -> 2,804 filesystem
calls (5%). The walk it removes is cheap because it was already depth- and
node_modules-bounded; the real win is WSL, where a 14-root scan of 31
skills drops from 45 `find` invocations to 14 (-69%).

Removing the field is a Rule 3 wire change per
docs/reference/remote-wire-compatibility.md — an old client reading it now
sees undefined. Safe here because the only reader was unreachable UI in
every shipped version, and it is deleted in the previous commit. The
cross-version harness covers the terminal stream only, so this reasoning is
the artifact rather than a test.
@nwparker
nwparker requested a review from brennanb2025 as a code owner August 13, 2026 08:56
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 98779fd6-cbd6-4727-85f6-3cb1de7c15f6

📥 Commits

Reviewing files that changed from the base of the PR and between 98c7fa6 and 7ede8df.

📒 Files selected for processing (1)
  • src/renderer/src/store/slices/ui.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/store/slices/ui.test.ts

📝 Walkthrough

Walkthrough

The standalone Skills page, navigation actions, route rendering, filters, labels, and localized strings were removed. The skills top-level view was removed from shared view validation and UI state. Skill discovery no longer counts package files or includes fileCount in local or WSL results. Related unit, integration, and end-to-end fixtures and expectations were updated.

Mergeability Score: 🔵 Low · up to 7ede8

The PR is otherwise mergeable, but the added WSL protocol test can leave its temporary fixture behind after repeated or failed runs, creating a bounded risk of test interference; clean up the fixture as follow-up or with owner awareness.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the required sections and testing details, but it does not provide the required linked issue reference. Add a valid issue reference after "Fixes #" or document an approved exception to the repository requirement.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two primary changes: removing the unreachable Skills page and its unused file-count calculation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Readiness review found no defects but three gaps worth closing.

The WSL scan is a positional NUL-delimited protocol whose producer is a
bash script and whose consumer is a parser in the same file. Every test
hand-encoded the record, so the two sides can drift together with a bug —
this PR's own 6-to-5 field change was verified only by editing fixtures to
match. Adds a test that runs the real generated script and feeds its real
stdout to the real parser, so the field count is checked by execution
rather than by memory. Mutation-verified: re-adding a field to the script
fails it with 'unknown source'.

Also prunes four locale-override entries keyed on i18n keys this PR
deleted (nothing validates that override keys exist, which is why lint
stayed green), and pins the 'skills' -> 'terminal' hydration demotion by
name so the removal reads as a migration rather than an accident.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/skills/skill-discovery-wsl-script-roundtrip.test.ts (1)

12-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the protocol comment.

The rationale is useful, but this block also describes test flow and implementation details. Keep only the protocol-drift rationale and the macOS timestamp caveat in one or two lines.

As per coding guidelines, comments in TypeScript must be concise, non-obvious, and brief—prefer one line; do not explain obvious behavior or walk through code.

Suggested comment reduction
-// Why: the WSL scan is a positional NUL-delimited protocol whose producer is a bash
-// script and whose consumer is a parser in the same file. Every other test hand-encodes
-// the record, so a field added to or removed from one side is only caught by whoever
-// remembers to edit the fixture — the two can drift together with the bug. This runs
-// the real generated script and feeds its real stdout to the real parser, so the field
-// count is checked by execution rather than by memory.
-//
-// The script is Linux-shaped but portable enough to run here: `stat -c` is GNU-only and
-// fails on macOS, which is why `updatedAt` is not asserted — an absent timestamp is
-// already the parser's documented degradation, not this test's subject.
+// Execute the generated script to catch producer/parser drift in the WSL field layout.
+// macOS may omit GNU `stat` timestamps, so `updatedAt` is not asserted here.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a8068db-d031-4707-b4be-b6262335356b

📥 Commits

Reviewing files that changed from the base of the PR and between b82e5c9 and 98c7fa6.

📒 Files selected for processing (5)
  • config/scripts/locale-cross-locale-key-overrides.mjs
  • config/scripts/locale-key-overrides.mjs
  • config/scripts/locale-ko-key-overrides.json
  • src/main/skills/skill-discovery-wsl-script-roundtrip.test.ts
  • src/renderer/src/store/slices/ui.test.ts
💤 Files with no reviewable changes (3)
  • config/scripts/locale-cross-locale-key-overrides.mjs
  • config/scripts/locale-key-overrides.mjs
  • config/scripts/locale-ko-key-overrides.json

Comment on lines +28 to +46
const base = await mkdtemp(join(tmpdir(), 'orca-wsl-roundtrip-'))
const presentRoot = join(base, 'skills')
await mkdir(join(presentRoot, 'review'), { recursive: true })
await writeFile(
join(presentRoot, 'review', 'SKILL.md'),
'---\nname: code-review\ndescription: Review code changes.\n---\n'
)
await mkdir(join(presentRoot, 'plan'), { recursive: true })
await writeFile(
join(presentRoot, 'plan', 'SKILL.md'),
'---\nname: planner\ndescription: Plan the work.\n---\n'
)
const roots = [root('present', presentRoot), root('missing', join(base, 'absent'))]

const { stdout } = await run('bash', ['-c', buildWslSkillDiscoveryCommand(roots)], {
encoding: 'utf8',
maxBuffer: 64 * 1024 * 1024
})
const result = parseWslSkillDiscoveryOutput(stdout, roots, 42)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the temporary fixture after the test.

mkdtemp creates base, but the test never removes it. Repeated or failed runs leave files in the OS temporary directory. Wrap setup, execution, and assertions in try/finally, then remove base with rm(..., { recursive: true, force: true }).

Suggested cleanup
-import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'
+import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
...
     const base = await mkdtemp(join(tmpdir(), 'orca-wsl-roundtrip-'))
+    try {
+      // Move the existing fixture setup, execution, and assertions here.
+    } finally {
+      await rm(base, { recursive: true, force: true })
+    }
🧰 Tools
🪛 ast-grep (0.45.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

Round-2 review noted the case started from the store default, which is
already 'terminal' — so it could not distinguish a demoted value from
hydration never running. Seeding 'tasks' first makes it fail when
'skills' is restored to TOP_LEVEL_VIEW_LOOKUP, which is the invariant it
exists to pin.

Co-authored-by: Orca <help@stably.ai>
@nwparker
nwparker merged commit 4c5f818 into main Aug 13, 2026
47 checks passed
@nwparker
nwparker deleted the nwparker/remove-orphaned-skills-page branch August 13, 2026 09:56
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.

1 participant