Repository navigation
fix(skills): restore the production skill catalog and stop --full from wiping it - #10
Merged
Merged
Conversation
…covery A `--full` run whose discovery set covered only a slice of the bundle soft-deprecated everything else. In production that left 27 of 293 skills active: `skill_route` answered every query from the ~19 survivors, so unrelated skills came back for every task while `skill_read` still worked fine for the missing ones — which is why it went unnoticed for days. The old guard only refused on `skills.length === 0`, so a catastrophically PARTIAL discovery sailed straight through. * Prune now refuses when it would deprecate >25% of the eligible active catalog and at least 10 skills; `--force` is the explicit override. The absolute floor keeps small/dev catalogs working (1 of 2 is a legitimate 50% prune). * New `--reactivate`: the exact inverse. Restores `deprecated` skills that ARE in the bundle. Genuinely retired skills never appear in the discovery set so they stay deprecated, and `pending` security-gate quarantines are never auto-approved. A redeploy alone cannot repair this — the boot import passes `status: NULL` and the upsert deliberately keeps existing status. * Collapse cross-zone duplicates before writing. The bundle ships 29 names in both data/skill/ and data/lifecycle-skills/; previously whichever copy was walked last silently decided the skill's type. Now deterministic and reported. * parseFrontmatter normalises line endings. Every pattern was anchored on \n, so a CRLF SKILL.md failed the opening `^---\n` match and lost its ENTIRE frontmatter — the skill fell back to its directory name and a placeholder description, stripping the trigger keywords routing ranks on. * skills stats() reports `active` / `byStatus` / `activeBy*` alongside the all-status totals it returned before. Reporting only all-status numbers is how a mass deprecation stayed invisible: stats said 293 while list/route could see 27. skill_stats now prints a warning banner when the active catalog drops below half the total. * codegraph gained a `typecheck` script — `pnpm -r typecheck` silently skipped it before (CI called tsc directly, so this was ergonomics only). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ills
seo-page and seo-programmatic were stored with CRLF line endings, so the
importer's `^---\n` match failed and both landed in the DB as
"Domain skill: <name>" with no trigger keywords — effectively unroutable.
The parser now tolerates CRLF (previous commit); these two files are
normalised so the repo is consistent either way.
expo-ui-swiftui and expo-ui-jetpack-compose self-reported title-case names
("Expo UI SwiftUI"), so they were keyed in the DB under a name no caller
would guess and `skill_read("expo-ui-swiftui")` missed them. Renamed to
match their directories; CATEGORY_MAP keeps the old keys so already-imported
rows retain their Mobile category instead of falling back to Other.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Only packages/codegraph/data/ is COPYed into the image, but these 29 domain skills existed solely in the .claude/skill workspace mirror, so they were never deployed. Confirmed against production: skill_read "stripe-subscriptions-webhooks" returned not found, and 3D/R3F queries had nothing relevant to match. Includes stripe-subscriptions-webhooks, supabase-auth-ssr, sentry-nextjs, resend-react-email, turborepo-monorepo, tanstack-query-next-actions, rhf-zod-server-actions, vitest-next-conventions, claude-api-patterns, the react-three-fiber / threejs / glsl / webgpu / webxr 3D set, and the ai-image-generation / ai-video-generation / tts-voiceover media set. The two mirrors are hand-maintained with no sync step, which is how they drifted; this closes the gap for the skills currently missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Production routing has been degraded for ~10 days.
skill_statsreported a healthy 293-skill catalog, but only 27 wereactive— and onlyactiveskills are ever listed or routed:The 19 surviving domain skills were the 17 marketing skills from #9 plus
nextjsand_routing-index, soskill_routeanswered every query from those:The same queries against a healthy catalog return
payments+stripe-subscriptions-webhooksandpayload+mongodb+cms. The ranking algorithm was never at fault — the data was.skill_readkept working for the missing skills, which is why nothing looked broken.Root cause
A
--fullprune whose discovery set covered only a slice of the bundle. The guard added in 5438d7a only refused onskills.length === 0, so a catastrophically partial discovery passed straight through and deprecated everything else. The survivors match the prune's own protection clause exactly (category NOT IN ('System','Lifecycle')) —_routing-indexand the 5 lifecycle skills.Changes
Prevention — prune now refuses when it would deprecate >25% of the eligible active catalog and ≥10 skills. The absolute floor keeps small catalogs working (1 of 2 is a legitimate 50% prune).
--forceis the explicit override.Recovery — new
--reactivate, the exact inverse of--full: restoresdeprecatedskills that ARE in the bundle. Genuinely retired skills never appear in the discovery set so they stay deprecated;pendingsecurity-gate quarantines are never auto-approved.Detection —
stats()counted every status whilelist/routeserve onlyactive. That contradiction is how this stayed invisible. It now reportsactive/byStatus/activeBy*, andskill_statsprints a warning banner when the active catalog drops below half the total.Deploy gap — 29 domain skills existed only in the
.claude/skillworkspace mirror and were never COPYed into the image. Confirmed against prod:skill_read("stripe-subscriptions-webhooks")→ not found. Now in the bundle.CRLF frontmatter —
seo-pageandseo-programmaticused CRLF, so^---\nfailed and both lost their entire frontmatter, landing in the DB as"Domain skill: <name>"with none of the trigger keywords routing ranks on. Parser now tolerates CRLF; both files normalised.Cross-zone duplicates — the bundle ships 29 names in both
data/skill/anddata/lifecycle-skills/; whichever copy was walked last silently decided the skill's type. Now collapsed deterministically and reported.Minor —
expo-ui-*renamed to kebab-case (they were keyed under"Expo UI SwiftUI", unreachable by the obvious name);typecheckscript added to codegraph (pnpm -r typecheckskipped it);/build/gitignored.Verification
Build 3/3, typecheck 3/3 (now including codegraph), 363 tests pass. Four new regression tests cover the partial-discovery guard,
--reactivatesemantics, CRLF frontmatter, and cross-zone collapse.End-to-end, against a workspace built exactly the way
entrypoint.shbuilds one in the container:--reactivate→ 315 active, the 2 security-gate quarantines correctly untouchedDeploying this
Merging + redeploying shows the 29 missing skills and installs the guard, but does not by itself repair the catalog — the boot import passes
status: NULLand the upsert deliberately keeps existing status rather than resurrecting deprecated rows. Recovery is an explicit step, in the container:The symlinks are required — without them discovery is empty and
--reactivaterestores 0 (harmless, but useless). Docs updated inDEPLOY-SKILLS.md.Left out deliberately
.claude/skill/pixarts/holds 6 real skills that are never imported (walkDirdoesn't recurse into nested dirs). They self-namepixarts/*, which the project's naming rule disallows — renaming them is a content decision, not a bug fix.webxr-spatial,shopify-expertsit inpendingon a skill-guard BLOCK verdict. By design those need human review.🤖 Generated with Claude Code