feat: add dark skills.sh catalog control plane (#3211)

Ships the fail-closed skills.sh catalog control plane validated by the bounded 500-row permanent Test gate. No production ingestion, schedule, visibility, or bulk scanning is enabled.
This commit is contained in:
Patrick Erichsen
2026-07-21 20:50:07 -07:00
committed by GitHub
parent 7ef2b15cfb
commit b34a0d69ff
31 changed files with 17996 additions and 63 deletions
+59 -2
View File
@@ -32,6 +32,7 @@
"@tanstack/react-router": "1.170.18",
"@tanstack/react-start": "1.168.32",
"@vercel/analytics": "2.0.1",
"@vercel/oidc": "^3.8.0",
"@vercel/speed-insights": "2.0.0",
"class-variance-authority": "0.7.1",
"clawhub-schema": "workspace:0.0.2",
@@ -67,6 +68,7 @@
"zod": "4.4.3",
},
"devDependencies": {
"@edge-runtime/vm": "^5.0.0",
"@faker-js/faker": "^10.5.0",
"@playwright/test": "^1.61.1",
"@react-email/ui": "^6.9.0",
@@ -82,6 +84,7 @@
"@typescript/native": "npm:typescript@7.0.2",
"@vitejs/plugin-react": "6.0.3",
"@vitest/coverage-v8": "4.1.10",
"convex-test": "^0.0.54",
"jsdom": "29.1.1",
"nitro": "3.0.260610-beta",
"only-allow": "1.2.2",
@@ -252,6 +255,10 @@
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="],
"@edge-runtime/primitives": ["@edge-runtime/primitives@6.0.0", "", {}, "sha512-FqoxaBT+prPBHBwE1WXS1ocnu/VLTQyZ6NMUBAdbP7N2hsFTTxMC/jMu2D/8GAlMQfxeuppcPuCUk/HO3fpIvA=="],
"@edge-runtime/vm": ["@edge-runtime/vm@5.0.0", "", { "dependencies": { "@edge-runtime/primitives": "6.0.0" } }, "sha512-NKBGBSIKUG584qrS1tyxVpX/AKJKQw5HgjYEnPLC0QsTw79JrGn+qUr8CXFb955Iy7GUdiiUv1rJ6JBGvaKb6w=="],
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
@@ -990,6 +997,12 @@
"@vercel/analytics": ["@vercel/analytics@2.0.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "nuxt": ">= 3", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "nuxt", "react", "svelte", "vue", "vue-router"] }, "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g=="],
"@vercel/cli-config": ["@vercel/cli-config@0.2.0", "", { "dependencies": { "xdg-app-paths": "5", "zod": "4.1.11" } }, "sha512-fJRRRB7734BDuXZ89yBEaA2ncYhH7bWX30mk04W80J6VAfQc+4iB8lyzAdaGpFV3/vNlkt9VZt+/uoQoWX6UsQ=="],
"@vercel/cli-exec": ["@vercel/cli-exec@1.0.0", "", { "dependencies": { "execa": "5.1.1" } }, "sha512-kQF8LGie/Hbdq9/psJxLE7owRTcqMQMhgybU04gCeR7cbQAr5t8OrjefDNColJv1QSSucFt4pLwRiARVmlOnug=="],
"@vercel/oidc": ["@vercel/oidc@3.8.0", "", { "dependencies": { "@vercel/cli-config": "0.2.0", "@vercel/cli-exec": "1.0.0", "jose": "^5.9.6" } }, "sha512-r00laGW6Pv778RoR6M2NxX91ycSj+PBwVo+fOb9Bif+F0IyUKt25zrvBzfEzQpeAzbqOgPZyQibEWDdDFApd+A=="],
"@vercel/speed-insights": ["@vercel/speed-insights@2.0.0", "", { "peerDependencies": { "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "nuxt": ">= 3", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@sveltejs/kit", "next", "nuxt", "react", "svelte", "vue", "vue-router"] }, "sha512-jwkNcrTeafWxjmWq4AHBaptSqZiJkYU5adLC9QBSqeim0GcqDMgN5Ievh8OG1rJ6W3A4l1oiP7qr9CWxGuzu3w=="],
"@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg=="],
@@ -1104,6 +1117,8 @@
"convex-helpers": ["convex-helpers@0.1.120", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "convex": "^1.32.0", "hono": "^4.0.5", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "typescript": "^5.5 || ^6.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@standard-schema/spec", "hono", "react", "typescript", "zod"], "bin": { "convex-helpers": "bin.cjs" } }, "sha512-FPqtmN/10uoxmmhVq0ViNi08AnmHKT/eU1R8+ohmPZoHfg8noZEZCltApY/ASDdhq92o0kLZETggzBvMfHJDLQ=="],
"convex-test": ["convex-test@0.0.54", "", { "peerDependencies": { "convex": "^1.32.0" } }, "sha512-C0v2SQcuxrELAJRNzE6fQ686XDPor7UFoC22jcoJXeCRqhLo8ZIMZ4jyUHoo1UdAL2enCb0AbiprCVpLDN8u9Q=="],
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
"cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="],
@@ -1112,6 +1127,8 @@
"cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"crossws": ["crossws@0.4.9", "", { "peerDependencies": { "srvx": ">=0.11.5" }, "optionalPeers": ["srvx"] }, "sha512-iWx+1OMSG2aOHpjyf9AESOzkwsVdS49cXM9dVrI2PDhxU5l2RIWE/KG56gk4BbAnsMoycvniJ9OnOxO9LRzHVA=="],
"css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
@@ -1262,6 +1279,8 @@
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
"expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
"exsolve": ["exsolve@1.1.0", "", {}, "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw=="],
@@ -1294,6 +1313,8 @@
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
"get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
"glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
@@ -1344,6 +1365,8 @@
"httpxy": ["httpxy@0.5.5", "", {}, "sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA=="],
"human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"ignore": ["ignore@7.0.6", "", {}, "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw=="],
@@ -1370,10 +1393,14 @@
"is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
"isbot": ["isbot@5.1.41", "", {}, "sha512-9WFV/Vhh0FEj6CQ7MoHweEL9/vLKPjeoD2I2htbAjX7kbW7VJs3OCpWOVyd+JraNTWVU6/DRx2MZy2KaUNXHcg=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="],
"istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="],
@@ -1492,6 +1519,8 @@
"mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="],
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
"mermaid": ["mermaid@11.16.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.20", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "katex": "^0.16.45", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA=="],
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
@@ -1556,6 +1585,8 @@
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
@@ -1582,6 +1613,8 @@
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
"nypm": ["nypm@0.6.6", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.1.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q=="],
"oauth4webapi": ["oauth4webapi@3.8.6", "", {}, "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ=="],
@@ -1598,7 +1631,7 @@
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
"onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
@@ -1608,6 +1641,8 @@
"ora": ["ora@9.4.1", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" } }, "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw=="],
"os-paths": ["os-paths@4.4.0", "", {}, "sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg=="],
"oxc-parser": ["oxc-parser@0.120.0", "", { "dependencies": { "@oxc-project/types": "^0.120.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.120.0", "@oxc-parser/binding-android-arm64": "0.120.0", "@oxc-parser/binding-darwin-arm64": "0.120.0", "@oxc-parser/binding-darwin-x64": "0.120.0", "@oxc-parser/binding-freebsd-x64": "0.120.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.120.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.120.0", "@oxc-parser/binding-linux-arm64-gnu": "0.120.0", "@oxc-parser/binding-linux-arm64-musl": "0.120.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.120.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.120.0", "@oxc-parser/binding-linux-riscv64-musl": "0.120.0", "@oxc-parser/binding-linux-s390x-gnu": "0.120.0", "@oxc-parser/binding-linux-x64-gnu": "0.120.0", "@oxc-parser/binding-linux-x64-musl": "0.120.0", "@oxc-parser/binding-openharmony-arm64": "0.120.0", "@oxc-parser/binding-wasm32-wasi": "0.120.0", "@oxc-parser/binding-win32-arm64-msvc": "0.120.0", "@oxc-parser/binding-win32-ia32-msvc": "0.120.0", "@oxc-parser/binding-win32-x64-msvc": "0.120.0" } }, "sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w=="],
"oxfmt": ["oxfmt@0.59.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.59.0", "@oxfmt/binding-android-arm64": "0.59.0", "@oxfmt/binding-darwin-arm64": "0.59.0", "@oxfmt/binding-darwin-x64": "0.59.0", "@oxfmt/binding-freebsd-x64": "0.59.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.59.0", "@oxfmt/binding-linux-arm-musleabihf": "0.59.0", "@oxfmt/binding-linux-arm64-gnu": "0.59.0", "@oxfmt/binding-linux-arm64-musl": "0.59.0", "@oxfmt/binding-linux-ppc64-gnu": "0.59.0", "@oxfmt/binding-linux-riscv64-gnu": "0.59.0", "@oxfmt/binding-linux-riscv64-musl": "0.59.0", "@oxfmt/binding-linux-s390x-gnu": "0.59.0", "@oxfmt/binding-linux-x64-gnu": "0.59.0", "@oxfmt/binding-linux-x64-musl": "0.59.0", "@oxfmt/binding-openharmony-arm64": "0.59.0", "@oxfmt/binding-win32-arm64-msvc": "0.59.0", "@oxfmt/binding-win32-ia32-msvc": "0.59.0", "@oxfmt/binding-win32-x64-msvc": "0.59.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-Xqk6cPZS1yMvVa7OAuenaDZUsgMDutvvbZ9/L5gSvAfW64+WN4HVhgipLj5rVERbYQt8fLs9TopyZ1rU1XEG/w=="],
@@ -1628,6 +1663,8 @@
"path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
@@ -1756,13 +1793,17 @@
"sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.3", "@img/sharp-darwin-x64": "0.35.3", "@img/sharp-freebsd-wasm32": "0.35.3", "@img/sharp-libvips-darwin-arm64": "1.3.2", "@img/sharp-libvips-darwin-x64": "1.3.2", "@img/sharp-libvips-linux-arm": "1.3.2", "@img/sharp-libvips-linux-arm64": "1.3.2", "@img/sharp-libvips-linux-ppc64": "1.3.2", "@img/sharp-libvips-linux-riscv64": "1.3.2", "@img/sharp-libvips-linux-s390x": "1.3.2", "@img/sharp-libvips-linux-x64": "1.3.2", "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", "@img/sharp-libvips-linuxmusl-x64": "1.3.2", "@img/sharp-linux-arm": "0.35.3", "@img/sharp-linux-arm64": "0.35.3", "@img/sharp-linux-ppc64": "0.35.3", "@img/sharp-linux-riscv64": "0.35.3", "@img/sharp-linux-s390x": "0.35.3", "@img/sharp-linux-x64": "0.35.3", "@img/sharp-linuxmusl-arm64": "0.35.3", "@img/sharp-linuxmusl-x64": "0.35.3", "@img/sharp-webcontainers-wasm32": "0.35.3", "@img/sharp-win32-arm64": "0.35.3", "@img/sharp-win32-ia32": "0.35.3", "@img/sharp-win32-x64": "0.35.3" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"shell-quote": ["shell-quote@1.10.0", "", {}, "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA=="],
"shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="],
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
@@ -1804,6 +1845,8 @@
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
"strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
"stubborn-fs": ["stubborn-fs@2.0.0", "", { "dependencies": { "stubborn-utils": "^1.0.1" } }, "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA=="],
"stubborn-utils": ["stubborn-utils@1.0.2", "", {}, "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg=="],
@@ -1928,12 +1971,18 @@
"when-exit": ["when-exit@2.1.5", "", {}, "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
"xdg-app-paths": ["xdg-app-paths@5.5.1", "", { "dependencies": { "os-paths": "^4.0.1", "xdg-portable": "^7.2.0" } }, "sha512-hI3flOB4PLZIy5prbtTpirobtPE2ZtZ52szO+2mM9Efp6ErM398La+C1lIpNWDfNoQk+6Lsi6nMcCwVB7pxeMQ=="],
"xdg-portable": ["xdg-portable@7.3.0", "", { "dependencies": { "os-paths": "^4.0.1" } }, "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw=="],
"xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
"xmlbuilder2": ["xmlbuilder2@4.0.3", "", { "dependencies": { "@oozcitak/dom": "^2.0.2", "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0", "js-yaml": "^4.1.1" } }, "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA=="],
@@ -2010,6 +2059,10 @@
"@types/ws/@types/node": ["@types/node@25.9.2", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw=="],
"@vercel/cli-config/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@vercel/oidc/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"ast-v8-to-istanbul/js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="],
@@ -2064,6 +2117,10 @@
"react-email/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
"restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"rolldown/@oxc-project/types": ["@oxc-project/types@0.134.0", "", {}, "sha512-T0xuRRKrQFmocH8y+jGfpmSkGcheaJExY9lEihmR1Gm2aH+75B8CzgU2rABRQSzzDxLjZ15Sc0bRVLj5lVeNXQ=="],
"strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+8
View File
@@ -41,6 +41,7 @@ import type * as httpApiV1_packagesV1 from "../httpApiV1/packagesV1.js";
import type * as httpApiV1_promotionsV1 from "../httpApiV1/promotionsV1.js";
import type * as httpApiV1_publishersV1 from "../httpApiV1/publishersV1.js";
import type * as httpApiV1_shared from "../httpApiV1/shared.js";
import type * as httpApiV1_skillsShCatalogV1 from "../httpApiV1/skillsShCatalogV1.js";
import type * as httpApiV1_skillsV1 from "../httpApiV1/skillsV1.js";
import type * as httpApiV1_starsV1 from "../httpApiV1/starsV1.js";
import type * as httpApiV1_transfersV1 from "../httpApiV1/transfersV1.js";
@@ -129,6 +130,8 @@ import type * as lib_skillTags from "../lib/skillTags.js";
import type * as lib_skillZip from "../lib/skillZip.js";
import type * as lib_skills_index from "../lib/skills/index.js";
import type * as lib_skills_slugResolution from "../lib/skills/slugResolution.js";
import type * as lib_skillsShCatalogEnvironment from "../lib/skillsShCatalogEnvironment.js";
import type * as lib_skillsShCatalogFixtures from "../lib/skillsShCatalogFixtures.js";
import type * as lib_staticPublishScan from "../lib/staticPublishScan.js";
import type * as lib_testSeed from "../lib/testSeed.js";
import type * as lib_tokens from "../lib/tokens.js";
@@ -163,6 +166,7 @@ import type * as skillCards from "../skillCards.js";
import type * as skillStatEvents from "../skillStatEvents.js";
import type * as skillTransfers from "../skillTransfers.js";
import type * as skills from "../skills.js";
import type * as skillsShCatalog from "../skillsShCatalog.js";
import type * as stars from "../stars.js";
import type * as statsMaintenance from "../statsMaintenance.js";
import type * as telemetry from "../telemetry.js";
@@ -212,6 +216,7 @@ declare const fullApi: ApiFromModules<{
"httpApiV1/promotionsV1": typeof httpApiV1_promotionsV1;
"httpApiV1/publishersV1": typeof httpApiV1_publishersV1;
"httpApiV1/shared": typeof httpApiV1_shared;
"httpApiV1/skillsShCatalogV1": typeof httpApiV1_skillsShCatalogV1;
"httpApiV1/skillsV1": typeof httpApiV1_skillsV1;
"httpApiV1/starsV1": typeof httpApiV1_starsV1;
"httpApiV1/transfersV1": typeof httpApiV1_transfersV1;
@@ -300,6 +305,8 @@ declare const fullApi: ApiFromModules<{
"lib/skillZip": typeof lib_skillZip;
"lib/skills/index": typeof lib_skills_index;
"lib/skills/slugResolution": typeof lib_skills_slugResolution;
"lib/skillsShCatalogEnvironment": typeof lib_skillsShCatalogEnvironment;
"lib/skillsShCatalogFixtures": typeof lib_skillsShCatalogFixtures;
"lib/staticPublishScan": typeof lib_staticPublishScan;
"lib/testSeed": typeof lib_testSeed;
"lib/tokens": typeof lib_tokens;
@@ -334,6 +341,7 @@ declare const fullApi: ApiFromModules<{
skillStatEvents: typeof skillStatEvents;
skillTransfers: typeof skillTransfers;
skills: typeof skills;
skillsShCatalog: typeof skillsShCatalog;
stars: typeof stars;
statsMaintenance: typeof statsMaintenance;
telemetry: typeof telemetry;
File diff suppressed because it is too large Load Diff
+13
View File
@@ -59,6 +59,7 @@ import {
verifyDocsSessionV1Http,
whoamiV1Http,
contentRightsV1Http,
skillsShCatalogTestV1Http,
} from "./httpApiV1";
import { preflightHandler } from "./httpPreflight";
import { installRateLimitedRoutes } from "./lib/httpRouteRateLimit";
@@ -324,6 +325,18 @@ http.route({
handler: contentRightsV1Http,
});
http.route({
path: "/api/v1/operator/skills-sh/catalog-test",
method: "GET",
handler: skillsShCatalogTestV1Http,
});
http.route({
path: "/api/v1/operator/skills-sh/catalog-test",
method: "POST",
handler: skillsShCatalogTestV1Http,
});
http.route({
pathPrefix: `${ApiRoutes.contentRights}/`,
method: "POST",
+3
View File
@@ -27,6 +27,7 @@ import {
promotionsPostRouterV1Handler,
} from "./httpApiV1/promotionsV1";
import { createPublisherV1Handler } from "./httpApiV1/publishersV1";
import { skillsShCatalogTestV1Handler } from "./httpApiV1/skillsShCatalogV1";
import {
exportSkillsV1Handler,
listSkillsV1Handler,
@@ -67,6 +68,7 @@ export const listBundlePluginsV1Http = httpAction(listBundlePluginsV1Handler);
export const verifyDocsSessionV1Http = httpAction(verifyDocsSessionV1Handler);
export const createPublisherV1Http = httpAction(createPublisherV1Handler);
export const contentRightsV1Http = httpAction(contentRightsV1Handler);
export const skillsShCatalogTestV1Http = httpAction(skillsShCatalogTestV1Handler);
export const catalogFeedV1Http = httpAction(catalogFeedV1Handler);
export const catalogSkillsFeedV1Http = httpAction(catalogSkillsFeedV1Handler);
export const promotionsFeedV1Http = httpAction(promotionsFeedV1Handler);
@@ -116,6 +118,7 @@ export const __handlers = {
verifyDocsSessionV1Handler,
createPublisherV1Handler,
contentRightsV1Handler,
skillsShCatalogTestV1Handler,
catalogFeedV1Handler,
catalogSkillsFeedV1Handler,
searchSkillsV1Handler,
+377
View File
@@ -0,0 +1,377 @@
/* @vitest-environment node */
import { createHash } from "node:crypto";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../lib/httpRateLimit", () => ({
applyRateLimit: vi.fn(async () => ({ ok: true, headers: {} })),
}));
vi.mock("../lib/githubAuth", () => ({
buildGitHubApiHeaders: vi.fn(async () => ({ Authorization: "Bearer placeholder" })),
}));
vi.mock("./shared", async (importOriginal) => {
const original = await importOriginal<typeof import("./shared")>();
return {
...original,
requireApiTokenUserOrResponse: vi.fn(),
requireAdminOrResponse: vi.fn(),
};
});
const { requireAdminOrResponse, requireApiTokenUserOrResponse } = await import("./shared");
const { buildGitHubApiHeaders } = await import("../lib/githubAuth");
const { skillsShCatalogTestV1Handler } = await import("./skillsShCatalogV1");
function sha256(value: string) {
return createHash("sha256").update(value).digest("hex");
}
function artifact(externalId: string, content: string) {
const fileHash = sha256(content);
return {
externalId,
artifactContentHash: sha256(`SKILL.md\0${fileHash}\n`),
files: [
{
path: "SKILL.md",
contentBase64: Buffer.from(content).toString("base64"),
sha256: fileHash,
contentType: "text/markdown",
},
],
};
}
describe("skills.sh catalog Test HTTP API", () => {
beforeEach(() => {
vi.mocked(requireApiTokenUserOrResponse).mockResolvedValue({
ok: true,
user: { handle: "catalog-operator" },
userId: "users:operator",
} as never);
vi.mocked(requireAdminOrResponse).mockReturnValue({ ok: true } as never);
});
afterEach(() => {
vi.unstubAllGlobals();
});
it("deletes uploaded files for admissions the mutation skips", async () => {
const storedIds = ["storage:linked", "storage:skipped"];
const store = vi.fn(async () => storedIds.shift()!);
const deleteStorage = vi.fn(async () => undefined);
const runAction = vi.fn(async (_ref, args: Record<string, unknown>) => {
expect(args).toMatchObject({
externalIds: ["nvidia/skills/aiq-deploy", "nvidia/skills/aiq-toolkit"],
});
return {
requested: 2,
admitted: 1,
skipped: 1,
admittedExternalIds: ["nvidia/skills/aiq-deploy"],
};
});
const ctx = {
runQuery: vi.fn(async () => ({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})),
runAction,
storage: {
store,
delete: deleteStorage,
},
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "admit",
runId: "skillsShCatalogRuns:test",
externalIds: ["nvidia/skills/aiq-deploy", "nvidia/skills/aiq-toolkit"],
artifacts: [
artifact("nvidia/skills/aiq-deploy", "# Linked"),
artifact("nvidia/skills/aiq-toolkit", "# Skipped"),
],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({ admitted: 1, skipped: 1 });
expect(store).toHaveBeenCalledTimes(2);
expect(deleteStorage).toHaveBeenCalledTimes(1);
expect(deleteStorage).toHaveBeenCalledWith("storage:skipped");
});
it("does not report a committed admission as failed when skipped-file cleanup fails", async () => {
const storedIds = ["storage:linked", "storage:skipped"];
const store = vi.fn(async () => storedIds.shift()!);
const deleteStorage = vi.fn(async () => {
throw new Error("temporary storage cleanup outage");
});
const ctx = {
runQuery: vi.fn(async () => ({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})),
runAction: vi.fn(async () => ({
requested: 2,
admitted: 1,
skipped: 1,
admittedExternalIds: ["nvidia/skills/aiq-deploy"],
})),
storage: {
store,
delete: deleteStorage,
},
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "admit",
runId: "skillsShCatalogRuns:test",
externalIds: ["nvidia/skills/aiq-deploy", "nvidia/skills/aiq-toolkit"],
artifacts: [
artifact("nvidia/skills/aiq-deploy", "# Linked"),
artifact("nvidia/skills/aiq-toolkit", "# Skipped"),
],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({ admitted: 1, skipped: 1 });
expect(deleteStorage).toHaveBeenCalledWith("storage:skipped");
});
it("reuses authenticated staging-live owner ids without a GitHub fetch", async () => {
const githubFetch = vi.fn();
vi.stubGlobal("fetch", githubFetch);
const ctx = {
runQuery: vi
.fn()
.mockResolvedValueOnce({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})
.mockResolvedValueOnce({
provenance: "stored-authenticated-staging-live",
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
missingOwners: [],
}),
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "resolve-owners",
owners: ["nvidia", "anthropics"],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(200);
expect(await response.json()).toEqual({
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live",
fetches: 0,
reused: 2,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
});
expect(githubFetch).not.toHaveBeenCalled();
expect(buildGitHubApiHeaders).not.toHaveBeenCalled();
});
it("fetches only owners missing from authenticated staging-live state", async () => {
const githubFetch = vi.fn(async (url: string, init?: RequestInit) => {
expect(init?.headers).toMatchObject({ Authorization: "Bearer placeholder" });
const owner = url.split("/").at(-1)!;
return new Response(
JSON.stringify({
id: owner === "nvidia" ? 1_728_152 : 76_263_028,
login: owner,
}),
);
});
vi.stubGlobal("fetch", githubFetch);
const ctx = {
runQuery: vi
.fn()
.mockResolvedValueOnce({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})
.mockResolvedValueOnce({
provenance: "stored-authenticated-staging-live",
owners: [{ owner: "nvidia", login: "nvidia", id: 1_728_152 }],
missingOwners: ["anthropics"],
})
.mockResolvedValueOnce({
provenance: "stored-authenticated-staging-live-assignment-check",
checked: 1,
}),
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "resolve-owners",
owners: ["nvidia", "anthropics"],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(200);
const body = await response.json();
expect(body).toEqual({
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live+live-github",
fetches: 1,
reused: 1,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
});
expect(githubFetch).toHaveBeenCalledTimes(1);
expect(buildGitHubApiHeaders).toHaveBeenCalledWith({
userAgent: "clawhub/skills-sh-catalog-test",
allowAnonymous: false,
useGitHubApp: false,
});
expect(JSON.stringify(body)).not.toContain("Bearer placeholder");
});
it("reports only non-secret HTTP status when authenticated owner lookup fails", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () => new Response("token=secret-response-body", { status: 404 })),
);
const ctx = {
runQuery: vi
.fn()
.mockResolvedValueOnce({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})
.mockResolvedValueOnce({
provenance: "stored-authenticated-staging-live",
owners: [],
missingOwners: ["neondatabase"],
}),
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "resolve-owners",
owners: ["neondatabase"],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
const body = await response.text();
expect(response.status).toBe(400);
expect(body).toBe("Authenticated GitHub owner lookup failed with HTTP 404: neondatabase");
expect(body).not.toContain("secret-response-body");
expect(body).not.toContain("Bearer placeholder");
});
it("fails closed when a new owner lacks authenticated GitHub access", async () => {
vi.mocked(buildGitHubApiHeaders).mockRejectedValueOnce(
new Error("GitHub API authentication is not configured"),
);
const githubFetch = vi.fn();
vi.stubGlobal("fetch", githubFetch);
const ctx = {
runQuery: vi
.fn()
.mockResolvedValueOnce({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})
.mockResolvedValueOnce({
provenance: "stored-authenticated-staging-live",
owners: [],
missingOwners: ["new-owner"],
}),
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "resolve-owners",
owners: ["new-owner"],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(400);
expect(await response.text()).toBe("GitHub API authentication is not configured");
expect(githubFetch).not.toHaveBeenCalled();
});
it("rejects a fetched owner id already assigned to another login", async () => {
const githubFetch = vi.fn(
async () =>
new Response(JSON.stringify({ id: 1_728_152, login: "renamed-nvidia" }), {
status: 200,
}),
);
vi.stubGlobal("fetch", githubFetch);
const ctx = {
runQuery: vi
.fn()
.mockResolvedValueOnce({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})
.mockResolvedValueOnce({
provenance: "stored-authenticated-staging-live",
owners: [],
missingOwners: ["renamed-nvidia"],
})
.mockRejectedValueOnce(
new Error("Authenticated GitHub owner id 1728152 is already assigned to another owner"),
),
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "resolve-owners",
owners: ["renamed-nvidia"],
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(400);
expect(await response.text()).toBe(
"Authenticated GitHub owner id 1728152 is already assigned to another owner",
);
expect(githubFetch).toHaveBeenCalledTimes(1);
});
});
+334
View File
@@ -0,0 +1,334 @@
import { internal } from "../_generated/api";
import type { Id } from "../_generated/dataModel";
import type { ActionCtx } from "../_generated/server";
import { buildGitHubApiHeaders } from "../lib/githubAuth";
import { applyRateLimit } from "../lib/httpRateLimit";
import { json, requireAdminOrResponse, requireApiTokenUserOrResponse, text } from "./shared";
const internalRefs = internal as unknown as {
skillsShCatalog: {
admitRealScansInternal: unknown;
assertFreshGitHubOwnerAssignmentsInternal: unknown;
getStagingLiveControlInternal: unknown;
processStagingLiveBatchInternal: unknown;
resolveKnownGitHubOwnersInternal: unknown;
startStagingLiveRunInternal: unknown;
};
};
const MAX_GITHUB_OWNER_RESOLUTIONS = 500;
const GITHUB_OWNER_RESOLUTION_CONCURRENCY = 8;
async function runMutationRef<T>(
ctx: ActionCtx,
ref: unknown,
args: Record<string, unknown>,
): Promise<T> {
return (await ctx.runMutation(ref as never, args as never)) as T;
}
async function runActionRef<T>(ctx: ActionCtx, ref: unknown, args: Record<string, unknown>) {
return (await ctx.runAction(ref as never, args as never)) as T;
}
async function runQueryRef<T>(
ctx: ActionCtx,
ref: unknown,
args: Record<string, unknown>,
): Promise<T> {
return (await ctx.runQuery(ref as never, args as never)) as T;
}
function asRecord(value: unknown): Record<string, unknown> | null {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: null;
}
function requireString(record: Record<string, unknown>, key: string) {
const value = record[key];
if (typeof value !== "string" || !value.trim()) throw new Error(`${key} is required`);
return value;
}
function requireNumber(record: Record<string, unknown>, key: string) {
const value = record[key];
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} is required`);
return value;
}
async function sha256Hex(bytes: Uint8Array) {
const digest = await crypto.subtle.digest("SHA-256", new Uint8Array(bytes).buffer);
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}
function decodeBase64(value: string) {
const binary = atob(value);
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
}
function normalizeGitHubOwners(ownersValue: unknown) {
if (!Array.isArray(ownersValue)) throw new Error("owners is required");
const owners = Array.from(
new Set(
ownersValue.map((owner) => {
if (typeof owner !== "string" || !owner.trim()) {
throw new Error("owner must be a non-empty string");
}
return owner.trim().toLowerCase();
}),
),
).sort();
if (owners.length < 1 || owners.length > MAX_GITHUB_OWNER_RESOLUTIONS) {
throw new Error(`owners must contain between 1 and ${MAX_GITHUB_OWNER_RESOLUTIONS} entries`);
}
return owners;
}
async function fetchAuthenticatedGitHubOwners(owners: string[]) {
if (owners.length === 0) return [];
const headers = await buildGitHubApiHeaders({
userAgent: "clawhub/skills-sh-catalog-test",
allowAnonymous: false,
useGitHubApp: false,
});
const resolved: Array<{ owner: string; id: number; login: string }> = [];
for (let offset = 0; offset < owners.length; offset += GITHUB_OWNER_RESOLUTION_CONCURRENCY) {
const batch = owners.slice(offset, offset + GITHUB_OWNER_RESOLUTION_CONCURRENCY);
resolved.push(
...(await Promise.all(
batch.map(async (owner) => {
const response = await fetch(
`https://api.github.com/users/${encodeURIComponent(owner)}`,
{
headers,
},
);
if (!response.ok) {
throw new Error(
`Authenticated GitHub owner lookup failed with HTTP ${response.status}: ${owner}`,
);
}
const payload = (await response.json()) as { id?: unknown; login?: unknown };
const id = typeof payload.id === "number" ? payload.id : Number.NaN;
const login = typeof payload.login === "string" ? payload.login.trim().toLowerCase() : "";
if (!Number.isSafeInteger(id) || id <= 0 || login !== owner) {
throw new Error(
`Authenticated GitHub owner lookup returned invalid identity: ${owner}`,
);
}
return { owner, id, login };
}),
)),
);
}
return resolved;
}
async function resolveAuthenticatedGitHubOwners(ctx: ActionCtx, ownersValue: unknown) {
const owners = normalizeGitHubOwners(ownersValue);
const known = await runQueryRef<{
provenance: "stored-authenticated-staging-live";
owners: Array<{ owner: string; id: number; login: string }>;
missingOwners: string[];
}>(ctx, internalRefs.skillsShCatalog.resolveKnownGitHubOwnersInternal, { owners });
const fetched = await fetchAuthenticatedGitHubOwners(known.missingOwners);
if (fetched.length > 0) {
await runQueryRef(ctx, internalRefs.skillsShCatalog.assertFreshGitHubOwnerAssignmentsInternal, {
owners: fetched.map(({ owner, id }) => ({ owner, id })),
});
}
const resolved = [...known.owners, ...fetched].sort((left, right) =>
left.owner.localeCompare(right.owner),
);
if (resolved.length !== owners.length) {
throw new Error("GitHub owner resolution did not return complete coverage");
}
return {
authentication: "clawhub-github-authenticated" as const,
provenance:
known.owners.length === 0
? ("live-github" as const)
: fetched.length === 0
? ("stored-authenticated-staging-live" as const)
: ("stored-authenticated-staging-live+live-github" as const),
fetches: fetched.length,
reused: known.owners.length,
owners: resolved,
};
}
async function storeArtifactFiles(
ctx: ActionCtx,
artifactsValue: unknown,
): Promise<{
artifacts: Array<{
externalId: string;
artifactContentHash: string;
files: Array<{
path: string;
size: number;
storageId: Id<"_storage">;
sha256: string;
contentType?: string;
}>;
}>;
storageIds: Id<"_storage">[];
}> {
if (!Array.isArray(artifactsValue) || artifactsValue.length < 1 || artifactsValue.length > 10) {
throw new Error("artifacts must contain between 1 and 10 entries");
}
const storageIds: Id<"_storage">[] = [];
const artifacts = [];
const externalIds = new Set<string>();
try {
for (const artifactValue of artifactsValue) {
const artifact = asRecord(artifactValue);
if (!artifact) throw new Error("artifact must be an object");
const externalId = requireString(artifact, "externalId").trim().toLowerCase();
if (externalIds.has(externalId)) throw new Error(`duplicate artifact: ${externalId}`);
externalIds.add(externalId);
const filesValue = artifact.files;
if (!Array.isArray(filesValue) || filesValue.length < 1 || filesValue.length > 100) {
throw new Error("artifact files must contain between 1 and 100 entries");
}
const files = [];
const filePaths = new Set<string>();
for (const fileValue of filesValue) {
const file = asRecord(fileValue);
if (!file) throw new Error("artifact file must be an object");
const path = requireString(file, "path");
if (filePaths.has(path)) throw new Error(`duplicate artifact file path: ${path}`);
filePaths.add(path);
const contentBase64 = requireString(file, "contentBase64");
const bytes = decodeBase64(contentBase64);
const declaredSha256 = requireString(file, "sha256").toLowerCase();
if ((await sha256Hex(bytes)) !== declaredSha256) {
throw new Error(`artifact file hash mismatch: ${path}`);
}
const contentType =
typeof file.contentType === "string" && file.contentType.trim()
? file.contentType.trim()
: undefined;
const storageId = await ctx.storage.store(
new Blob([bytes], { type: contentType ?? "application/octet-stream" }),
);
storageIds.push(storageId);
files.push({
path,
size: bytes.byteLength,
storageId,
sha256: declaredSha256,
...(contentType ? { contentType } : {}),
});
}
files.sort((left, right) => left.path.localeCompare(right.path));
const artifactContentHash = requireString(artifact, "artifactContentHash").toLowerCase();
const manifest = files.map((file) => `${file.path}\0${file.sha256}\n`).join("");
const computedArtifactHash = await sha256Hex(new TextEncoder().encode(manifest));
if (computedArtifactHash !== artifactContentHash) {
throw new Error(`artifact manifest hash mismatch: ${externalId}`);
}
artifacts.push({
externalId,
artifactContentHash,
files,
});
}
} catch (error) {
await Promise.allSettled(
storageIds.map(async (storageId) => await ctx.storage.delete(storageId)),
);
throw error;
}
return { artifacts, storageIds };
}
export async function skillsShCatalogTestV1Handler(ctx: ActionCtx, request: Request) {
const rate = await applyRateLimit(ctx, request, request.method === "GET" ? "read" : "write");
if (!rate.ok) return rate.response;
const auth = await requireApiTokenUserOrResponse(ctx, request, rate.headers);
if (!auth.ok) return auth.response;
const admin = requireAdminOrResponse(auth.user, rate.headers);
if (!admin.ok) return admin.response;
try {
const staging = await runQueryRef<{
environment: "test";
deploymentName: string | null;
buildSha: string | null;
control: Record<string, unknown>;
}>(ctx, internalRefs.skillsShCatalog.getStagingLiveControlInternal, {});
if (request.method === "GET") return json(staging, 200, rate.headers);
if (request.method !== "POST") return text("Not found", 404, rate.headers);
const body = asRecord(await request.json());
if (!body) return text("Invalid JSON", 400, rate.headers);
const operation = requireString(body, "operation");
if (operation === "resolve-owners") {
return json(await resolveAuthenticatedGitHubOwners(ctx, body.owners), 200, rate.headers);
}
if (operation === "start") {
const result = await runMutationRef(
ctx,
internalRefs.skillsShCatalog.startStagingLiveRunInternal,
{
actor: auth.user.handle,
reason: requireString(body, "reason"),
snapshotId: requireString(body, "snapshotId"),
sourceCapturedAt: requireString(body, "sourceCapturedAt"),
snapshotCaptureFetches: requireNumber(body, "snapshotCaptureFetches"),
fixtureLength: requireNumber(body, "fixtureLength"),
},
);
return json(result, 200, rate.headers);
}
if (operation === "batch") {
if (!Array.isArray(body.rows)) throw new Error("rows is required");
const result = await runMutationRef(
ctx,
internalRefs.skillsShCatalog.processStagingLiveBatchInternal,
{
runId: requireString(body, "runId"),
cursor: requireNumber(body, "cursor"),
rows: body.rows,
},
);
return json(result, 200, rate.headers);
}
if (operation === "admit") {
if (!Array.isArray(body.externalIds)) throw new Error("externalIds is required");
const stored = await storeArtifactFiles(ctx, body.artifacts);
let result: {
admittedExternalIds: string[];
[key: string]: unknown;
};
try {
result = await runActionRef(ctx, internalRefs.skillsShCatalog.admitRealScansInternal, {
runId: requireString(body, "runId"),
externalIds: body.externalIds,
actorUserId: auth.userId,
artifacts: stored.artifacts,
});
} catch (error) {
await Promise.allSettled(
stored.storageIds.map(async (storageId) => await ctx.storage.delete(storageId)),
);
throw error;
}
const admittedExternalIds = new Set(result.admittedExternalIds);
const unlinkedStorageIds = stored.artifacts
.filter((artifact) => !admittedExternalIds.has(artifact.externalId))
.flatMap((artifact) => artifact.files.map((file) => file.storageId));
await Promise.allSettled(
unlinkedStorageIds.map(async (storageId) => await ctx.storage.delete(storageId)),
);
return json(result, 200, rate.headers);
}
return text("Unknown operation", 400, rate.headers);
} catch (error) {
const message = error instanceof Error ? error.message : "skills.sh Test operation failed";
const unavailable =
message.includes("permanent Test") || message.includes("available only in permanent Test");
return text(unavailable ? "Not found" : message, unavailable ? 404 : 400, rate.headers);
}
}
+6
View File
@@ -53,6 +53,12 @@ describe("retention policies", () => {
});
});
it("keeps skills.sh scan attempts as durable exact-hash deduplication records", () => {
expect(getRetentionPolicy("skillsShCatalogScanAttempts")).toMatchObject({
classification: "permanent",
});
});
it("documents publisher abuse signals as durable review evidence", () => {
expect(getRetentionPolicy("publisherAbuseSignals")).toMatchObject({
classification: "permanent",
+6
View File
@@ -215,6 +215,12 @@ export const RETENTION_POLICIES = {
promotions: permanent("Curated promotional offers; ended records stay for launch-page history."),
auditLogs: permanent("Audit logs are durable compliance/security history."),
systemSettings: permanent("Durable operator-controlled system settings."),
skillsShCatalogControls: permanent("Durable skills.sh catalog operator controls."),
skillsShCatalogRuns: permanent("Skills.sh catalog run, cursor, and rollback audit history."),
skillsShCatalogEntries: permanent("Normalized unclaimed skills.sh catalog identities."),
skillsShCatalogScanAttempts: permanent(
"Exact-hash skills.sh scan attempts are durable audit and deduplication history.",
),
publisherAbuseScoreRuns: permanent("Abuse scoring run history."),
publisherAbuseTemporalScanSamples: ephemeral(
"Exact temporal percentile samples are temporary scan working state.",
@@ -0,0 +1,116 @@
/* @vitest-environment node */
import { describe, expect, it } from "vitest";
import { getSkillsShFixtureEnvironmentPolicy } from "./skillsShCatalogEnvironment";
describe("skills.sh fixture environment policy", () => {
it("allows only local development or the exact cron-disabled Test deployment", () => {
expect(
getSkillsShFixtureEnvironmentPolicy({
CONVEX_CLOUD_URL: "http://127.0.0.1:3210",
}),
).toEqual({ allowed: true, environment: "local" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "academic-chihuahua-392",
CLAWHUB_DISABLE_CRONS: "1",
CLAWHUB_ENV: "test",
CONVEX_CLOUD_URL: "https://academic-chihuahua-392.convex.cloud",
}),
).toEqual({ allowed: true, environment: "test" });
});
it("rejects previews, production, and incomplete Test markers", () => {
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_PREVIEW: "1",
CONVEX_DEPLOYMENT: "anonymous:clawhub",
}),
).toMatchObject({ allowed: false, environment: "preview" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "academic-chihuahua-392",
CLAWHUB_ENV: "test",
}),
).toMatchObject({ allowed: false, environment: "test" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "wry-manatee-359",
CLAWHUB_DISABLE_CRONS: "1",
CLAWHUB_ENV: "test",
CONVEX_CLOUD_URL: "https://academic-chihuahua-392.convex.cloud",
}),
).toMatchObject({ allowed: false, environment: "test" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CONVEX_DEPLOYMENT: "prod:wry-manatee-359",
}),
).toMatchObject({ allowed: false, environment: "production" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "wry-manatee-359",
CONVEX_CLOUD_URL: "http://127.0.0.1:3210",
}),
).toMatchObject({ allowed: false, environment: "production" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "academic-chihuahua-392",
CLAWHUB_DISABLE_CRONS: "1",
CLAWHUB_ENV: "test",
CONVEX_DEPLOYMENT: "prod:wry-manatee-359",
CONVEX_CLOUD_URL: "https://academic-chihuahua-392.convex.cloud",
}),
).toMatchObject({ allowed: false, environment: "production" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "academic-chihuahua-392",
CLAWHUB_DISABLE_CRONS: "1",
CLAWHUB_ENV: "test",
CONVEX_CLOUD_URL: "https://preview-project.convex.cloud",
}),
).toMatchObject({ allowed: false, environment: "test" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "academic-chihuahua-392",
CLAWHUB_DISABLE_CRONS: "1",
CLAWHUB_ENV: "test",
CLAWHUB_PREVIEW: "1",
CONVEX_CLOUD_URL: "https://academic-chihuahua-392.convex.cloud",
}),
).toMatchObject({ allowed: false, environment: "preview" });
});
it("does not treat CLI-only local deployment markers as runtime proof", () => {
expect(
getSkillsShFixtureEnvironmentPolicy({
CONVEX_DEPLOYMENT: "local:clawhub",
}),
).toMatchObject({ allowed: false, environment: "unknown" });
});
it("rejects conflicting populated Convex runtime URLs", () => {
expect(
getSkillsShFixtureEnvironmentPolicy({
CLAWHUB_DEPLOYMENT_NAME: "academic-chihuahua-392",
CLAWHUB_DISABLE_CRONS: "1",
CLAWHUB_ENV: "test",
CONVEX_CLOUD_URL: "https://academic-chihuahua-392.convex.cloud",
CONVEX_SITE_URL: "https://preview-project.convex.site",
}),
).toMatchObject({ allowed: false, environment: "test" });
expect(
getSkillsShFixtureEnvironmentPolicy({
CONVEX_CLOUD_URL: "http://127.0.0.1:3210",
CONVEX_SITE_URL: "https://academic-chihuahua-392.convex.site",
}),
).toMatchObject({ allowed: false, environment: "unknown" });
});
});
+130
View File
@@ -0,0 +1,130 @@
type SkillsShCatalogEnvironment = {
CLAWHUB_DEPLOYMENT_NAME?: string;
CLAWHUB_DISABLE_CRONS?: string;
CLAWHUB_ENV?: string;
CLAWHUB_PREVIEW?: string;
CONVEX_CLOUD_URL?: string;
CONVEX_DEPLOYMENT?: string;
CONVEX_SITE_URL?: string;
DEV_AUTH_CONVEX_DEPLOYMENT?: string;
};
export type SkillsShFixtureEnvironmentPolicy =
| {
allowed: true;
environment: "local" | "test";
}
| {
allowed: false;
environment: "preview" | "test" | "production" | "unknown";
reason: string;
};
const TEST_DEPLOYMENT = "academic-chihuahua-392";
const TEST_CLOUD_URL = `https://${TEST_DEPLOYMENT}.convex.cloud`;
const TEST_SITE_URL = `https://${TEST_DEPLOYMENT}.convex.site`;
function populatedRuntimeUrls(env: SkillsShCatalogEnvironment) {
return [
{ actual: env.CONVEX_CLOUD_URL?.trim(), expectedTest: TEST_CLOUD_URL },
{ actual: env.CONVEX_SITE_URL?.trim(), expectedTest: TEST_SITE_URL },
].filter((entry): entry is { actual: string; expectedTest: string } => Boolean(entry.actual));
}
function isLocalRuntimeUrl(value: string | undefined) {
if (!value) return false;
try {
const hostname = new URL(value).hostname;
return (
hostname === "localhost" ||
hostname === "127.0.0.1" ||
hostname === "0.0.0.0" ||
hostname === "[::1]"
);
} catch {
return false;
}
}
export function getSkillsShFixtureEnvironmentPolicy(
env: SkillsShCatalogEnvironment = process.env,
): SkillsShFixtureEnvironmentPolicy {
if (env.CLAWHUB_PREVIEW === "1") {
return {
allowed: false,
environment: "preview",
reason: "skills.sh catalog fixture work is disabled in Preview",
};
}
const deployment = env.CONVEX_DEPLOYMENT?.trim() || env.DEV_AUTH_CONVEX_DEPLOYMENT?.trim() || "";
if (deployment.startsWith("prod:")) {
return {
allowed: false,
environment: "production",
reason: "skills.sh catalog fixture work is disabled in production",
};
}
if (env.CLAWHUB_ENV === "test") {
if (env.CLAWHUB_DISABLE_CRONS !== "1") {
return {
allowed: false,
environment: "test",
reason: "skills.sh Test fixture work requires CLAWHUB_DISABLE_CRONS=1",
};
}
if (env.CLAWHUB_DEPLOYMENT_NAME !== TEST_DEPLOYMENT) {
return {
allowed: false,
environment: "test",
reason: `skills.sh Test fixture work requires CLAWHUB_DEPLOYMENT_NAME=${TEST_DEPLOYMENT}`,
};
}
const runtimeUrls = populatedRuntimeUrls(env);
if (
runtimeUrls.length === 0 ||
runtimeUrls.some((entry) => entry.actual !== entry.expectedTest)
) {
return {
allowed: false,
environment: "test",
reason: `skills.sh Test fixture work requires the ${TEST_DEPLOYMENT} Convex runtime URL`,
};
}
return { allowed: true, environment: "test" };
}
if (env.CLAWHUB_DEPLOYMENT_NAME) {
return {
allowed: false,
environment: "production",
reason: "skills.sh catalog fixture work is disabled in production",
};
}
const runtimeUrls = populatedRuntimeUrls(env);
if (runtimeUrls.length > 0 && runtimeUrls.every((entry) => isLocalRuntimeUrl(entry.actual))) {
return { allowed: true, environment: "local" };
}
return {
allowed: false,
environment: "unknown",
reason: "skills.sh catalog fixture work requires an explicit local or Test environment",
};
}
export function assertSkillsShFixtureEnvironmentAllowed(
env: SkillsShCatalogEnvironment = process.env,
) {
const policy = getSkillsShFixtureEnvironmentPolicy(env);
if (!policy.allowed) throw new Error(policy.reason);
return policy;
}
export function assertSkillsShCatalogControlMutationAllowed(
env: SkillsShCatalogEnvironment = process.env,
) {
if (env.CLAWHUB_PREVIEW === "1") {
throw new Error("skills.sh catalog control mutations are disabled in Preview");
}
}
+184
View File
@@ -0,0 +1,184 @@
import frozenSnapshot from "../fixtures/skills-sh-500-2026-07-21.json";
export type SkillsShCatalogFixtureRow = {
externalId: string;
githubOwnerId: number;
owner: string;
repo: string;
slug: string;
displayName: string;
sourceUrl: string;
githubRepoUrl: string;
sourceContentHash: string;
installs: number;
};
type SkillsShCatalogFixture = {
snapshotId: string;
sourceKind: "fixture" | "frozen-snapshot";
capturedAt: string | null;
snapshotCaptureFetches: number;
length: number;
rowAt: (index: number) => SkillsShCatalogFixtureRow;
findByExternalId: (externalId: string) => SkillsShCatalogFixtureRow | null;
};
const AIQ_DEPLOY: SkillsShCatalogFixtureRow = {
externalId: "nvidia/skills/aiq-deploy",
githubOwnerId: 1_728_152,
owner: "nvidia",
repo: "skills",
slug: "aiq-deploy",
displayName: "AIQ Deploy",
sourceUrl: "https://www.skills.sh/nvidia/skills/aiq-deploy",
githubRepoUrl: "https://github.com/NVIDIA/skills",
sourceContentHash: "c087fae29cc3882e03116c37921a7e092c42b15a04aa9a6c503e000a3e260ab5",
installs: 100,
};
const AIQ_DEPLOY_V2: SkillsShCatalogFixtureRow = {
...AIQ_DEPLOY,
displayName: "AIQ Deploy Updated",
sourceContentHash: "d087fae29cc3882e03116c37921a7e092c42b15a04aa9a6c503e000a3e260ab5",
installs: 101,
};
const FROZEN_ROWS = frozenSnapshot.rows satisfies SkillsShCatalogFixtureRow[];
function fromRows(
metadata: Omit<SkillsShCatalogFixture, "length" | "rowAt" | "findByExternalId">,
rows: readonly SkillsShCatalogFixtureRow[],
): SkillsShCatalogFixture {
const byExternalId = new Map(rows.map((row) => [row.externalId, row]));
return {
...metadata,
length: rows.length,
rowAt(index) {
const row = rows[index];
if (!row) throw new Error(`skills.sh fixture row ${index} is out of bounds`);
return row;
},
findByExternalId(externalId) {
return byExternalId.get(externalId) ?? null;
},
};
}
function changedFrozenRows() {
return FROZEN_ROWS.map((row, index) => {
if (index === 0) {
return {
...row,
displayName: `${row.displayName} Updated`,
sourceContentHash: `${row.sourceContentHash.slice(0, -1)}${
row.sourceContentHash.endsWith("0") ? "1" : "0"
}`,
installs: row.installs + 1,
};
}
if (index === 1) {
return {
...row,
installs: row.installs + 1,
};
}
return row;
});
}
const SYNTHETIC_DISCOVERY_LENGTH = 20_000;
function syntheticDiscoveryRow(index: number): SkillsShCatalogFixtureRow {
if (!Number.isInteger(index) || index < 0 || index >= SYNTHETIC_DISCOVERY_LENGTH) {
throw new Error(`skills.sh synthetic fixture row ${index} is out of bounds`);
}
const suffix = index.toString().padStart(5, "0");
return {
externalId: `synthetic-owner/synthetic-repo/skill-${suffix}`,
githubOwnerId: 9_999_999,
owner: "synthetic-owner",
repo: "synthetic-repo",
slug: `skill-${suffix}`,
displayName: `Synthetic Skill ${suffix}`,
sourceUrl: `https://www.skills.sh/synthetic-owner/synthetic-repo/skill-${suffix}`,
githubRepoUrl: "https://github.com/synthetic-owner/synthetic-repo",
sourceContentHash: index.toString(16).padStart(64, "0"),
installs: index,
};
}
const FIXTURES = {
"nvidia-small-v1": fromRows(
{
snapshotId: "nvidia-small-v1",
sourceKind: "fixture",
capturedAt: null,
snapshotCaptureFetches: 0,
},
[
AIQ_DEPLOY,
{ ...AIQ_DEPLOY },
{
externalId: "nvidia/skills/cuda-agent",
githubOwnerId: 1_728_152,
owner: "nvidia",
repo: "skills",
slug: "cuda-agent",
displayName: "CUDA Agent",
sourceUrl: "https://www.skills.sh/nvidia/skills/cuda-agent",
githubRepoUrl: "https://github.com/NVIDIA/skills",
sourceContentHash: "b087fae29cc3882e03116c37921a7e092c42b15a04aa9a6c503e000a3e260ab5",
installs: 50,
},
],
),
"nvidia-small-v2": fromRows(
{
snapshotId: "nvidia-small-v2",
sourceKind: "fixture",
capturedAt: null,
snapshotCaptureFetches: 0,
},
[AIQ_DEPLOY_V2],
),
"skills-sh-500-2026-07-21": fromRows(
{
snapshotId: frozenSnapshot.snapshotId,
sourceKind: "frozen-snapshot",
capturedAt: frozenSnapshot.capturedAt,
snapshotCaptureFetches: frozenSnapshot.captureMetrics.skillsShFetches,
},
FROZEN_ROWS,
),
"skills-sh-500-2026-07-21-v2": fromRows(
{
snapshotId: `${frozenSnapshot.snapshotId}-v2`,
sourceKind: "frozen-snapshot",
capturedAt: frozenSnapshot.capturedAt,
snapshotCaptureFetches: frozenSnapshot.captureMetrics.skillsShFetches,
},
changedFrozenRows(),
),
"synthetic-20000-v1": {
snapshotId: "synthetic-20000-v1",
sourceKind: "fixture",
capturedAt: null,
snapshotCaptureFetches: 0,
length: SYNTHETIC_DISCOVERY_LENGTH,
rowAt: syntheticDiscoveryRow,
findByExternalId(externalId) {
const match = /^synthetic-owner\/synthetic-repo\/skill-(\d{5})$/.exec(externalId);
if (!match) return null;
const index = Number(match[1]);
return index < SYNTHETIC_DISCOVERY_LENGTH ? syntheticDiscoveryRow(index) : null;
},
},
} satisfies Record<string, SkillsShCatalogFixture>;
export type SkillsShCatalogFixtureId = keyof typeof FIXTURES;
export function getSkillsShCatalogFixture(
fixtureId: SkillsShCatalogFixtureId,
): SkillsShCatalogFixture {
return FIXTURES[fixtureId];
}
+182
View File
@@ -715,6 +715,7 @@ const securityScanJobSourceValidator = v.union(
v.literal("backfill"),
v.literal("bulk-rescan"),
v.literal("manual"),
v.literal("skills-sh-catalog-test"),
);
const skillCardGenerationJobStatusValidator = v.union(
v.literal("queued"),
@@ -780,6 +781,7 @@ const skillScanRequestSourceKindValidator = v.union(
v.literal("upload"),
v.literal("published"),
v.literal("github"),
v.literal("skills-sh-catalog"),
);
const skills = defineTable({
@@ -1917,6 +1919,7 @@ const skillScanRequests = defineTable({
skillId: v.optional(v.id("skills")),
skillVersionId: v.optional(v.id("skillVersions")),
githubSkillScanId: v.optional(v.id("githubSkillScans")),
skillsShCatalogAttemptId: v.optional(v.id("skillsShCatalogScanAttempts")),
files: packageFilesValidator,
fileChunkCount: v.optional(v.number()),
fileManifestBytes: v.optional(v.number()),
@@ -2751,6 +2754,181 @@ const systemSettings = defineTable({
updatedByUserId: v.optional(v.id("users")),
}).index("by_key_and_updated_at", ["key", "updatedAt"]);
const skillsShCatalogControls = defineTable({
key: v.literal("global"),
mode: v.union(v.literal("off"), v.literal("fixture"), v.literal("staging-live")),
discoveryEnabled: v.boolean(),
writesEnabled: v.boolean(),
scanPlanningEnabled: v.boolean(),
scanAdmissionEnabled: v.boolean(),
publicVisibilityEnabled: v.boolean(),
paused: v.boolean(),
maxEntriesPerRun: v.number(),
maxEntriesPerBatch: v.number(),
maxWritesPerBatch: v.number(),
maxPlannedScans: v.number(),
maxScanAdmissionsPerBatch: v.number(),
maxScanAdmissionsPerRun: v.number(),
maxScanAdmissionsPerDay: v.number(),
maxCatalogQueued: v.number(),
maxCatalogInFlight: v.number(),
maxNativeQueued: v.number(),
maxNativeInFlight: v.number(),
realScanAllowlist: v.array(v.string()),
updatedBy: v.string(),
reason: v.string(),
updatedAt: v.number(),
}).index("by_key", ["key"]);
const skillsShCatalogRunCountsValidator = v.object({
observed: v.number(),
wouldInsert: v.number(),
wouldUpdate: v.number(),
inserted: v.number(),
updated: v.number(),
unchanged: v.number(),
rejected: v.number(),
scansPlanned: v.number(),
scansAdmitted: v.number(),
scansCompleted: v.number(),
scansCanceled: v.number(),
});
const skillsShCatalogRuns = defineTable({
fixtureId: v.union(
v.literal("nvidia-small-v1"),
v.literal("nvidia-small-v2"),
v.literal("skills-sh-500-2026-07-21"),
v.literal("skills-sh-500-2026-07-21-v2"),
v.literal("synthetic-20000-v1"),
v.literal("skills-sh-test-live-500"),
),
snapshotId: v.string(),
sourceKind: v.union(
v.literal("fixture"),
v.literal("frozen-snapshot"),
v.literal("staging-live"),
),
sourceCapturedAt: v.optional(v.string()),
snapshotCaptureFetches: v.number(),
dryRun: v.boolean(),
status: v.union(
v.literal("running"),
v.literal("paused"),
v.literal("completed"),
v.literal("budget-exhausted"),
v.literal("failed"),
v.literal("canceling"),
v.literal("canceled"),
),
cursor: v.number(),
scanCursor: v.number(),
fixtureLength: v.number(),
counts: skillsShCatalogRunCountsValidator,
budgets: v.object({
maxEntriesPerRun: v.number(),
maxEntriesPerBatch: v.number(),
maxWritesPerBatch: v.number(),
maxPlannedScans: v.number(),
maxScanAdmissionsPerBatch: v.number(),
maxScanAdmissionsPerRun: v.number(),
maxScanAdmissionsPerDay: v.number(),
}),
operations: v.object({
functionCalls: v.number(),
dbReads: v.number(),
dbWrites: v.number(),
}),
actor: v.string(),
reason: v.string(),
lastError: v.optional(v.string()),
batchesProcessed: v.number(),
scanAdmissionBatches: v.number(),
lastBatchWrites: v.number(),
lastBatchReads: v.number(),
startedAt: v.number(),
completedAt: v.optional(v.number()),
updatedAt: v.number(),
})
.index("by_started_at", ["startedAt"])
.index("by_status_and_updated_at", ["status", "updatedAt"]);
const skillsShCatalogEntries = defineTable({
externalId: v.string(),
sourceKind: v.union(
v.literal("fixture"),
v.literal("frozen-snapshot"),
v.literal("staging-live"),
),
githubOwnerId: v.number(),
owner: v.string(),
repo: v.string(),
slug: v.string(),
displayName: v.string(),
sourceUrl: v.string(),
githubRepoUrl: v.string(),
sourceContentHash: v.string(),
installs: v.number(),
sourceSnapshotId: v.string(),
publicVisible: v.boolean(),
scanStatus: v.union(
v.literal("not-planned"),
v.literal("planned"),
v.literal("queued"),
v.literal("clean"),
v.literal("suspicious"),
v.literal("malicious"),
v.literal("failed"),
v.literal("canceled"),
),
firstObservedAt: v.number(),
lastObservedAt: v.number(),
createdAt: v.number(),
updatedAt: v.number(),
})
.index("by_external_id", ["externalId"])
.index("by_owner_and_source_kind_and_github_owner_id", ["owner", "sourceKind", "githubOwnerId"])
.index("by_source_kind_and_github_owner_id_and_owner", ["sourceKind", "githubOwnerId", "owner"])
.index("by_scan_status_and_updated_at", ["scanStatus", "updatedAt"]);
const skillsShCatalogScanAttempts = defineTable({
entryId: v.id("skillsShCatalogEntries"),
runId: v.id("skillsShCatalogRuns"),
externalId: v.string(),
sourceContentHash: v.string(),
artifactContentHash: v.optional(v.string()),
skillScanRequestId: v.optional(v.id("skillScanRequests")),
securityScanJobId: v.optional(v.id("securityScanJobs")),
source: v.union(v.literal("skills-sh-catalog-fixture"), v.literal("skills-sh-catalog-test")),
dispatchKind: v.union(v.literal("deterministic"), v.literal("real")),
priority: v.literal("low"),
status: v.union(
v.literal("queued"),
v.literal("running"),
v.literal("succeeded"),
v.literal("failed"),
v.literal("canceled"),
),
verdict: v.optional(
v.union(
v.literal("clean"),
v.literal("suspicious"),
v.literal("malicious"),
v.literal("failed"),
),
),
completedAt: v.optional(v.number()),
createdAt: v.number(),
updatedAt: v.number(),
})
.index("by_entry_and_source_content_hash", ["entryId", "sourceContentHash"])
.index("by_run", ["runId"])
.index("by_run_and_status", ["runId", "status"])
.index("by_run_dispatch_kind_status_created_at", ["runId", "dispatchKind", "status", "createdAt"])
.index("by_created_at", ["createdAt"])
.index("by_status_and_created_at", ["status", "createdAt"])
.index("by_dispatch_kind_and_status_and_created_at", ["dispatchKind", "status", "createdAt"]);
const publisherAbuseScoreRuns = defineTable({
modelVersion: v.string(),
modelConfig: publisherAbuseModelConfigValidator,
@@ -3429,6 +3607,10 @@ export default defineSchema({
promotions,
auditLogs,
systemSettings,
skillsShCatalogControls,
skillsShCatalogRuns,
skillsShCatalogEntries,
skillsShCatalogScanAttempts,
publisherAbuseScoreRuns,
publisherAbuseTemporalScanSamples,
publisherAbuseTemporalScanCandidates,
File diff suppressed because it is too large Load Diff
+689 -41
View File
@@ -21,6 +21,7 @@ import {
MAX_SKILL_SCAN_REQUEST_MANIFEST_BYTES,
serializedSkillScanRequestFilesBytes,
} from "./lib/skillScanRequestFiles";
import { getSkillsShFixtureEnvironmentPolicy } from "./lib/skillsShCatalogEnvironment";
import { redactWorkerPublicText } from "./lib/workerTextRedaction";
import { requestSecurityScanDispatch } from "./securityScanDispatch";
@@ -138,10 +139,21 @@ const jobSourceValidator = v.union(
v.literal("backfill"),
v.literal("bulk-rescan"),
v.literal("manual"),
v.literal("skills-sh-catalog-test"),
);
type SecurityScanJobSource = "publish" | "vt-update" | "backfill" | "bulk-rescan" | "manual";
const codexScanWorkerLaneValidator = v.union(v.literal("priority"), v.literal("shared"));
type SecurityScanJobSource =
| "publish"
| "vt-update"
| "backfill"
| "bulk-rescan"
| "manual"
| "skills-sh-catalog-test";
const codexScanWorkerLaneValidator = v.union(
v.literal("priority"),
v.literal("shared"),
v.literal("catalog"),
);
type CodexScanQueueHealth = {
snapshotAt: number;
@@ -158,6 +170,7 @@ const CLAIM_SOURCE_ORDER: SecurityScanJobSource[] = [
"backfill",
"vt-update",
"bulk-rescan",
"skills-sh-catalog-test",
];
const SOURCE_PRIORITY: Record<SecurityScanJobSource, number> = {
@@ -166,6 +179,7 @@ const SOURCE_PRIORITY: Record<SecurityScanJobSource, number> = {
backfill: 3,
"vt-update": 2,
"bulk-rescan": 1,
"skills-sh-catalog-test": 0,
};
function higherPrioritySource(
@@ -310,6 +324,12 @@ const githubSkillScanStatusValidator = v.union(
v.literal("pending"),
v.literal("failed"),
);
const catalogScanVerdictValidator = v.union(
v.literal("clean"),
v.literal("suspicious"),
v.literal("malicious"),
v.literal("failed"),
);
const internalRefs = internal as unknown as {
packages: {
@@ -329,6 +349,7 @@ const internalRefs = internal as unknown as {
getSkillScanRequestForUserInternal: unknown;
getJobTargetInternal: unknown;
recordGitHubSkillScanResultInternal: unknown;
completeCatalogSkillScanJobInternal: unknown;
recordSkillScanRequestFailedInternal: unknown;
recordSkillScanRequestSucceededInternal: unknown;
requeueJobLeaseInternal: unknown;
@@ -1258,6 +1279,42 @@ async function enqueueSkillScanRequestJob(
return jobId;
}
export async function enqueueSkillsShCatalogScanRequest(
ctx: MutationCtx,
args: {
actorUserId: Id<"users">;
attemptId: Id<"skillsShCatalogScanAttempts">;
slug: string;
displayName: string;
artifactContentHash: string;
files: Doc<"skillScanRequests">["files"];
},
) {
const now = Date.now();
const requestId = await ctx.db.insert("skillScanRequests", {
actorUserId: args.actorUserId,
sourceKind: "skills-sh-catalog",
update: false,
writtenBack: false,
status: "queued",
requestedJobSource: "skills-sh-catalog-test",
requestedJobPriority: -100,
slug: args.slug,
displayName: args.displayName,
skillsShCatalogAttemptId: args.attemptId,
files: args.files,
sha256hash: args.artifactContentHash,
expiresAt: skillScanRequestExpiresAt(now),
createdAt: now,
updatedAt: now,
});
const jobId = await enqueueSkillScanRequestJob(ctx, requestId, {
source: "skills-sh-catalog-test",
priority: -100,
});
return { requestId, jobId };
}
export const prepareGitHubSkillScanRequestInternal = internalMutation({
args: {
skillId: v.id("skills"),
@@ -1868,6 +1925,227 @@ export const recordSkillScanRequestSucceededInternal = internalMutation({
},
});
export const completeCatalogSkillScanJobInternal = internalMutation({
args: {
attemptId: v.id("skillsShCatalogScanAttempts"),
scanId: v.id("skillScanRequests"),
jobId: v.id("securityScanJobs"),
leaseToken: v.string(),
artifactContentHash: v.string(),
verdict: catalogScanVerdictValidator,
runId: v.optional(v.string()),
llmAnalysis: llmAnalysisValidator,
skillSpectorAnalysis: v.optional(skillSpectorAnalysisValidator),
},
handler: async (ctx, args) => {
const environment = getSkillsShFixtureEnvironmentPolicy();
if (!environment.allowed || environment.environment !== "test") {
throw new ConvexError("catalog scan completion requires the permanent Test environment");
}
const [job, request, attempt] = await Promise.all([
ctx.db.get(args.jobId),
ctx.db.get(args.scanId),
ctx.db.get(args.attemptId),
]);
if (
!job ||
job.source !== "skills-sh-catalog-test" ||
job.targetKind !== "skillScanRequest" ||
job.skillScanRequestId !== args.scanId
) {
throw new ConvexError("Catalog scan job linkage mismatch");
}
if (
!request ||
request.sourceKind !== "skills-sh-catalog" ||
request.securityScanJobId !== args.jobId ||
request.skillsShCatalogAttemptId !== args.attemptId
) {
throw new ConvexError("Catalog scan request linkage mismatch");
}
if (
!attempt ||
attempt.dispatchKind !== "real" ||
attempt.skillScanRequestId !== args.scanId ||
attempt.securityScanJobId !== args.jobId
) {
throw new ConvexError("Catalog scan attempt linkage mismatch");
}
const artifactContentHash = args.artifactContentHash.toLowerCase();
if (
!attempt.artifactContentHash ||
attempt.artifactContentHash !== artifactContentHash ||
request.sha256hash !== artifactContentHash
) {
throw new ConvexError("Catalog scan artifact hash mismatch");
}
if (
attempt.status === "succeeded" ||
attempt.status === "failed" ||
attempt.status === "canceled"
) {
const expectedStatus = args.verdict === "failed" ? "failed" : "succeeded";
if (
attempt.status === expectedStatus &&
attempt.verdict === args.verdict &&
request.status === expectedStatus &&
job.status === expectedStatus &&
(expectedStatus !== "failed" ||
(request.lastError === "Catalog scan analysis failed" &&
job.lastError === "Catalog scan analysis failed"))
) {
return { ok: true as const, applied: true as const, publicVisible: false as const };
}
if (
attempt.status === "canceled" &&
request.status === "failed" &&
job.status === "failed" &&
request.lastError === job.lastError
) {
if (request.lastError === "Catalog run canceled before scan completion") {
return { ok: true as const, applied: false as const, reason: "run-canceled" as const };
}
if (request.lastError === "Catalog source changed before scan completion") {
return { ok: true as const, applied: false as const, reason: "stale-attempt" as const };
}
}
throw new ConvexError("Catalog scan terminal result mismatch");
}
if (
job.leaseToken !== args.leaseToken ||
job.status !== "running" ||
(attempt.status !== "queued" && attempt.status !== "running")
) {
throw new ConvexError("Catalog scan job lease mismatch");
}
const [run, entry] = await Promise.all([
ctx.db.get(attempt.runId),
ctx.db.get(attempt.entryId),
]);
const now = Date.now();
const terminalizeWithoutResult = async (reason: "run-canceled" | "stale-attempt") => {
const entryStillCurrent = entry?.sourceContentHash === attempt.sourceContentHash;
await ctx.db.patch(attempt._id, {
status: "canceled",
completedAt: now,
updatedAt: now,
});
if (entryStillCurrent) {
await ctx.db.patch(entry._id, {
scanStatus: "canceled",
publicVisible: false,
updatedAt: now,
});
}
await ctx.db.patch(request._id, {
status: "failed",
lastError:
reason === "run-canceled"
? "Catalog run canceled before scan completion"
: "Catalog source changed before scan completion",
completedAt: now,
updatedAt: now,
});
await ctx.db.patch(job._id, {
status: "failed",
lastError:
reason === "run-canceled"
? "Catalog run canceled before scan completion"
: "Catalog source changed before scan completion",
completedAt: now,
leaseToken: undefined,
leaseExpiresAt: undefined,
updatedAt: now,
});
if (run) {
const [queued, running] = await Promise.all([
ctx.db
.query("skillsShCatalogScanAttempts")
.withIndex("by_run_and_status", (q) => q.eq("runId", run._id).eq("status", "queued"))
.first(),
ctx.db
.query("skillsShCatalogScanAttempts")
.withIndex("by_run_and_status", (q) => q.eq("runId", run._id).eq("status", "running"))
.first(),
]);
await ctx.db.patch(run._id, {
...(reason === "run-canceled"
? { status: queued || running ? ("canceling" as const) : ("canceled" as const) }
: {}),
counts: {
...run.counts,
scansCanceled: run.counts.scansCanceled + 1,
},
operations: {
functionCalls: run.operations.functionCalls + 1,
dbReads: run.operations.dbReads + 7,
dbWrites: run.operations.dbWrites + (entryStillCurrent ? 5 : 4),
},
updatedAt: now,
});
}
return { ok: true as const, applied: false as const, reason };
};
if (run?.status === "canceling" || run?.status === "canceled") {
return await terminalizeWithoutResult("run-canceled");
}
if (!entry || entry.sourceContentHash !== attempt.sourceContentHash) {
return await terminalizeWithoutResult("stale-attempt");
}
const scanFailed = args.verdict === "failed";
await ctx.db.patch(attempt._id, {
status: scanFailed ? "failed" : "succeeded",
verdict: args.verdict,
completedAt: now,
updatedAt: now,
});
await ctx.db.patch(entry._id, {
scanStatus: args.verdict,
publicVisible: false,
updatedAt: now,
});
await ctx.db.patch(request._id, {
status: scanFailed ? "failed" : "succeeded",
lastError: scanFailed ? "Catalog scan analysis failed" : undefined,
llmAnalysis: args.llmAnalysis,
...(args.skillSpectorAnalysis
? { skillSpectorAnalysis: capSkillSpectorAnalysisForStorage(args.skillSpectorAnalysis) }
: {}),
writtenBack: request.writtenBack,
runId: args.runId,
completedAt: now,
updatedAt: now,
});
await ctx.db.patch(job._id, {
status: scanFailed ? "failed" : "succeeded",
lastError: scanFailed ? "Catalog scan analysis failed" : undefined,
runId: args.runId,
completedAt: now,
leaseToken: undefined,
leaseExpiresAt: undefined,
updatedAt: now,
});
if (run) {
await ctx.db.patch(run._id, {
counts: {
...run.counts,
scansCompleted: run.counts.scansCompleted + 1,
},
operations: {
functionCalls: run.operations.functionCalls + 1,
dbReads: run.operations.dbReads + 5,
dbWrites: run.operations.dbWrites + 5,
},
updatedAt: now,
});
}
return { ok: true as const, applied: true as const, publicVisible: false as const };
},
});
export const recordSkillScanRequestFailedInternal = internalMutation({
args: {
scanId: v.id("skillScanRequests"),
@@ -1945,12 +2223,32 @@ export const pruneExpiredSkillScanRequestsInternal = internalMutation({
let deletedRequests = 0;
let deferredRequests = 0;
for (const request of requests) {
if (request.securityScanJobId) {
const job = await ctx.db.get(request.securityScanJobId);
if (job?.targetKind === "skillScanRequest") {
await ctx.db.delete(job._id);
deletedJobs += 1;
}
const job = request.securityScanJobId ? await ctx.db.get(request.securityScanJobId) : null;
if (
request.sourceKind === "skills-sh-catalog" &&
job?.targetKind === "skillScanRequest" &&
(job.status === "queued" || job.status === "running")
) {
deferredRequests += 1;
continue;
}
if (request.sourceKind === "skills-sh-catalog" && request.skillsShCatalogAttemptId) {
const attempt = await ctx.db.get(request.skillsShCatalogAttemptId);
const run = attempt ? await ctx.db.get(attempt.runId) : null;
await terminalizeBlockedCatalogRetry(
ctx,
{
kind: "blocked",
request,
...(attempt ? { attempt } : {}),
...(run ? { run } : {}),
},
now,
);
}
if (job?.targetKind === "skillScanRequest") {
await ctx.db.delete(job._id);
deletedJobs += 1;
}
const fileChunks =
request.sourceKind === "github"
@@ -1977,7 +2275,11 @@ export const pruneExpiredSkillScanRequestsInternal = internalMutation({
deferredRequests += 1;
continue;
}
if (request.sourceKind === "upload" || request.sourceKind === "github") {
if (
request.sourceKind === "upload" ||
request.sourceKind === "github" ||
request.sourceKind === "skills-sh-catalog"
) {
for (const file of [...request.files, ...fileChunks.flatMap((chunk) => chunk.files)]) {
try {
await ctx.storage.delete(file.storageId);
@@ -2369,9 +2671,35 @@ export const claimQueuedJobsInternal = internalMutation({
const ready: Doc<"securityScanJobs">[] = [];
const claimedIds = new Set<Id<"securityScanJobs">>();
const remainingCapacity = () => capacity - ready.length;
const addReadyJobs = (jobs: Doc<"securityScanJobs">[]) => {
let catalogGate:
| {
control: Doc<"skillsShCatalogControls">;
health: Awaited<ReturnType<typeof readCatalogClaimHealth>>;
}
| null
| undefined;
const loadCatalogGate = async () => {
if (catalogGate !== undefined) return catalogGate;
const environment = getSkillsShFixtureEnvironmentPolicy();
if (!environment.allowed || environment.environment !== "test") {
catalogGate = null;
return catalogGate;
}
const control = await ctx.db
.query("skillsShCatalogControls")
.withIndex("by_key", (q) => q.eq("key", "global"))
.unique();
catalogGate = control
? {
control,
health: await readCatalogClaimHealth(ctx, control),
}
: null;
return catalogGate;
};
const addReadyJobs = (jobs: Doc<"securityScanJobs">[], stopAtCapacity = true) => {
for (const job of jobs) {
if (remainingCapacity() === 0) break;
if (stopAtCapacity && remainingCapacity() === 0) break;
if (claimedIds.has(job._id) || job.nextRunAt > now) continue;
claimedIds.add(job._id);
ready.push(job);
@@ -2379,39 +2707,97 @@ export const claimQueuedJobsInternal = internalMutation({
};
const takeReadySourceJobs = async (source: SecurityScanJobSource) => {
if (remainingCapacity() === 0) return [];
let takeLimit = remainingCapacity();
if (source === "skills-sh-catalog-test") {
const gate = await loadCatalogGate();
if (
!gate ||
gate.control.mode !== "staging-live" ||
gate.control.paused ||
!gate.control.scanAdmissionEnabled ||
!gate.health.claimable
) {
return [];
}
// Scan a bounded window independent of the current admission cap so paused
// or canceled jobs cannot hide later runnable backlog after the cap is lowered.
takeLimit = MAX_CODEX_SCAN_CLAIM_LIMIT;
}
return await ctx.db
.query("securityScanJobs")
.withIndex("by_status_source_next_run_at", (q) =>
q.eq("status", "queued").eq("source", source).lte("nextRunAt", now),
)
.order("asc")
.take(remainingCapacity());
.take(takeLimit);
};
addReadyJobs(await takeReadySourceJobs("manual"));
if (args.lane === "catalog") {
addReadyJobs(await takeReadySourceJobs("skills-sh-catalog-test"), false);
} else {
addReadyJobs(await takeReadySourceJobs("manual"));
if (remainingCapacity() > 0) {
addReadyJobs(
await ctx.db
.query("securityScanJobs")
.withIndex("by_status_malicious_signal_next_run_at", (q) =>
q.eq("status", "queued").eq("hasMaliciousSignal", true).lte("nextRunAt", now),
)
.order("asc")
.take(remainingCapacity()),
);
}
if (remainingCapacity() > 0) {
addReadyJobs(
await ctx.db
.query("securityScanJobs")
.withIndex("by_status_malicious_signal_next_run_at", (q) =>
q.eq("status", "queued").eq("hasMaliciousSignal", true).lte("nextRunAt", now),
)
.order("asc")
.take(remainingCapacity()),
);
}
// Shared workers remain work-conserving and may help priority work. The dedicated
// priority lane never claims bulk sources, which guarantees reserved fast-path capacity.
for (const source of CLAIM_SOURCE_ORDER) {
addReadyJobs(await takeReadySourceJobs(source));
if (remainingCapacity() === 0) break;
if (args.lane === "priority" && source === "publish") break;
// Shared workers remain work-conserving and may help priority work. The dedicated
// priority lane never claims bulk sources, which guarantees reserved fast-path capacity.
for (const source of CLAIM_SOURCE_ORDER) {
addReadyJobs(await takeReadySourceJobs(source), source !== "skills-sh-catalog-test");
if (remainingCapacity() === 0) break;
if (args.lane === "priority" && source === "publish") break;
}
}
const claimed = [];
let catalogClaims = 0;
for (const job of ready) {
if (claimed.length >= capacity) break;
let catalogAttemptId: Id<"skillsShCatalogScanAttempts"> | null = null;
if (job.source === "skills-sh-catalog-test") {
if (!job.skillScanRequestId) {
continue;
}
const request = await ctx.db.get(job.skillScanRequestId);
const attempt = request?.skillsShCatalogAttemptId
? await ctx.db.get(request.skillsShCatalogAttemptId)
: null;
const run = attempt ? await ctx.db.get(attempt.runId) : null;
await loadCatalogGate();
const control = catalogGate?.control ?? null;
const health = catalogGate?.health ?? null;
if (
!request ||
request.sourceKind !== "skills-sh-catalog" ||
!attempt ||
attempt.skillScanRequestId !== request._id ||
attempt.securityScanJobId !== job._id ||
attempt.status !== "queued" ||
!run ||
run.status === "paused" ||
run.status === "canceling" ||
run.status === "canceled" ||
run.status === "failed" ||
!control ||
control.mode !== "staging-live" ||
control.paused ||
!control.scanAdmissionEnabled ||
!health?.claimable ||
health.catalogInFlight + catalogClaims >= control.maxCatalogInFlight
) {
continue;
}
catalogAttemptId = attempt._id;
}
const leaseToken = crypto.randomUUID();
await ctx.db.patch(job._id, {
status: "running",
@@ -2429,6 +2815,13 @@ export const claimQueuedJobsInternal = internalMutation({
updatedAt: now,
});
}
if (catalogAttemptId) {
await ctx.db.patch(catalogAttemptId, {
status: "running",
updatedAt: now,
});
catalogClaims += 1;
}
claimed.push({
...job,
status: "running" as const,
@@ -2442,6 +2835,169 @@ export const claimQueuedJobsInternal = internalMutation({
},
});
async function readCatalogClaimHealth(ctx: MutationCtx, control: Doc<"skillsShCatalogControls">) {
const nativeSources = ["publish", "vt-update", "backfill", "bulk-rescan", "manual"] as const;
const [nativeQueuedBySource, nativeRunningBySource, catalogQueued, catalogRunning] =
await Promise.all([
Promise.all(
nativeSources.map(async (source) =>
ctx.db
.query("securityScanJobs")
.withIndex("by_status_source_created_at", (q) =>
q.eq("status", "queued").eq("source", source),
)
.take(control.maxNativeQueued + 1),
),
),
Promise.all(
nativeSources.map(async (source) =>
ctx.db
.query("securityScanJobs")
.withIndex("by_status_source_created_at", (q) =>
q.eq("status", "running").eq("source", source),
)
.take(control.maxNativeInFlight + 1),
),
),
ctx.db
.query("skillsShCatalogScanAttempts")
.withIndex("by_dispatch_kind_and_status_and_created_at", (q) =>
q.eq("dispatchKind", "real").eq("status", "queued"),
)
.take(control.maxCatalogQueued + 1),
ctx.db
.query("skillsShCatalogScanAttempts")
.withIndex("by_dispatch_kind_and_status_and_created_at", (q) =>
q.eq("dispatchKind", "real").eq("status", "running"),
)
.take(control.maxCatalogInFlight + 1),
]);
const nativeQueued = Math.min(
control.maxNativeQueued + 1,
nativeQueuedBySource.reduce((count, jobs) => count + jobs.length, 0),
);
const nativeInFlight = Math.min(
control.maxNativeInFlight + 1,
nativeRunningBySource.reduce((count, jobs) => count + jobs.length, 0),
);
return {
nativeQueued,
nativeInFlight,
catalogQueued: catalogQueued.length,
catalogInFlight: catalogRunning.length,
// Queued depth is an admission limit, not a drain limit. Already admitted work
// must remain claimable after an operator lowers maxCatalogQueued.
claimable:
nativeQueued <= control.maxNativeQueued &&
nativeInFlight <= control.maxNativeInFlight &&
catalogRunning.length <= control.maxCatalogInFlight,
};
}
type CatalogRetryDecision =
| { kind: "not-catalog" }
| {
kind: "allowed";
attemptId: Id<"skillsShCatalogScanAttempts">;
requestId: Id<"skillScanRequests">;
}
| {
kind: "blocked";
request?: Doc<"skillScanRequests">;
attempt?: Doc<"skillsShCatalogScanAttempts">;
run?: Doc<"skillsShCatalogRuns">;
};
async function prepareCatalogRetry(
ctx: MutationCtx,
job: Doc<"securityScanJobs">,
now: number,
retryAllowed = true,
): Promise<CatalogRetryDecision> {
if (job.source !== "skills-sh-catalog-test") return { kind: "not-catalog" };
if (job.targetKind !== "skillScanRequest" || !job.skillScanRequestId) {
return { kind: "blocked" };
}
const request = await ctx.db.get(job.skillScanRequestId);
if (!request || request.sourceKind !== "skills-sh-catalog" || !request.skillsShCatalogAttemptId) {
return { kind: "blocked", ...(request ? { request } : {}) };
}
const attempt = await ctx.db.get(request.skillsShCatalogAttemptId);
if (
!attempt ||
attempt.skillScanRequestId !== request._id ||
attempt.securityScanJobId !== job._id
) {
return { kind: "blocked", request };
}
const run = await ctx.db.get(attempt.runId);
if (
!retryAllowed ||
(attempt.status !== "queued" && attempt.status !== "running") ||
!run ||
run.status === "canceling" ||
run.status === "canceled" ||
run.status === "failed"
) {
return { kind: "blocked", request, attempt, ...(run ? { run } : {}) };
}
await ctx.db.patch(attempt._id, {
status: "queued",
updatedAt: now,
});
return {
kind: "allowed",
attemptId: attempt._id,
requestId: request._id,
};
}
async function terminalizeBlockedCatalogRetry(
ctx: MutationCtx,
decision: Extract<CatalogRetryDecision, { kind: "blocked" }>,
now: number,
) {
const attempt = decision.attempt;
if (!attempt || (attempt.status !== "queued" && attempt.status !== "running")) return;
const canceled = decision.run?.status === "canceling" || decision.run?.status === "canceled";
await ctx.db.patch(attempt._id, {
status: canceled ? "canceled" : "failed",
verdict: canceled ? undefined : "failed",
completedAt: now,
updatedAt: now,
});
const entry = await ctx.db.get(attempt.entryId);
const entryShouldBeTerminalized =
entry?.sourceContentHash === attempt.sourceContentHash &&
(entry.scanStatus === "planned" || entry.scanStatus === "queued");
if (entryShouldBeTerminalized) {
await ctx.db.patch(entry._id, {
scanStatus: canceled ? "canceled" : "failed",
publicVisible: false,
updatedAt: now,
});
}
if (decision.run) {
await ctx.db.patch(decision.run._id, {
counts: {
...decision.run.counts,
scansCompleted: decision.run.counts.scansCompleted + (canceled ? 0 : 1),
scansCanceled: decision.run.counts.scansCanceled + (canceled ? 1 : 0),
},
operations: {
...decision.run.operations,
functionCalls: decision.run.operations.functionCalls + 1,
dbReads: decision.run.operations.dbReads + 4,
dbWrites: decision.run.operations.dbWrites + (entryShouldBeTerminalized ? 3 : 2),
},
updatedAt: now,
});
}
}
export const requeueExpiredCodexScanJobsInternal = internalMutation({
args: {
limit: v.optional(v.number()),
@@ -2459,7 +3015,30 @@ export const requeueExpiredCodexScanJobsInternal = internalMutation({
Math.min(args.limit ?? MAX_EXPIRED_CODEX_SCAN_LEASE_REQUEUES, MAX_CODEX_SCAN_CLAIM_LIMIT),
),
);
let requeued = 0;
for (const job of jobs) {
const catalogRetry = await prepareCatalogRetry(ctx, job, now);
if (catalogRetry.kind === "blocked") {
await terminalizeBlockedCatalogRetry(ctx, catalogRetry, now);
await ctx.db.patch(job._id, {
status: "failed",
lastError: "Catalog scan retry blocked by inactive or invalid linkage",
completedAt: now,
leaseToken: undefined,
leaseExpiresAt: undefined,
workerId: undefined,
updatedAt: now,
});
if (catalogRetry.request) {
await ctx.db.patch(catalogRetry.request._id, {
status: "failed",
lastError: "Catalog scan retry blocked by inactive or invalid linkage",
completedAt: now,
updatedAt: now,
});
}
continue;
}
await ctx.db.patch(job._id, {
status: "queued",
leaseToken: undefined,
@@ -2468,9 +3047,18 @@ export const requeueExpiredCodexScanJobsInternal = internalMutation({
nextRunAt: now,
updatedAt: now,
});
if (catalogRetry.kind === "allowed") {
await ctx.db.patch(catalogRetry.requestId, {
status: "queued",
lastError: undefined,
completedAt: undefined,
updatedAt: now,
});
}
requeued += 1;
}
if (jobs.length > 0) await requestSecurityScanDispatch(ctx);
return { requeued: jobs.length };
if (requeued > 0) await requestSecurityScanDispatch(ctx);
return { requeued };
},
});
@@ -2500,11 +3088,13 @@ export const requeueFailedSecurityScanJobsInternal = internalMutation({
.gte("updatedAt", args.failedAfter)
.lt("updatedAt", args.failedBefore),
)
.filter((q) => q.neq(q.field("source"), "skills-sh-catalog-test"))
.order("asc")
.take(limit + 1);
const matched = jobs.slice(0, limit);
const bySource: Partial<Record<SecurityScanJobSource, number>> = {};
const byTargetKind: Partial<Record<Doc<"securityScanJobs">["targetKind"], number>> = {};
let requeued = 0;
for (const job of matched) {
bySource[job.source] = (bySource[job.source] ?? 0) + 1;
@@ -2553,13 +3143,14 @@ export const requeueFailedSecurityScanJobsInternal = internalMutation({
}
}
}
requeued += 1;
}
if (!args.dryRun && matched.length > 0) await requestSecurityScanDispatch(ctx);
if (!args.dryRun && requeued > 0) await requestSecurityScanDispatch(ctx);
return {
dryRun: args.dryRun,
matched: matched.length,
requeued: args.dryRun ? 0 : matched.length,
requeued: args.dryRun ? 0 : requeued,
hasMore: jobs.length > limit,
bySource,
byTargetKind,
@@ -2672,8 +3263,12 @@ export const failJobInternal = internalMutation({
const job = await ctx.db.get(args.jobId);
if (!job || job.leaseToken !== args.leaseToken) throw new ConvexError("Lease mismatch");
const now = Date.now();
const retry = job.attempts < MAX_ATTEMPTS;
const catalogRetry = await prepareCatalogRetry(ctx, job, now, job.attempts < MAX_ATTEMPTS);
const retry = job.attempts < MAX_ATTEMPTS && catalogRetry.kind !== "blocked";
const error = sanitizeWorkerErrorDetail(args.error, 2000);
if (catalogRetry.kind === "blocked") {
await terminalizeBlockedCatalogRetry(ctx, catalogRetry, now);
}
await ctx.db.patch(args.jobId, {
status: retry ? "queued" : "failed",
lastError: error,
@@ -2712,22 +3307,39 @@ export const requeueJobLeaseInternal = internalMutation({
throw new ConvexError("Lease mismatch");
}
const now = Date.now();
const catalogRetry = await prepareCatalogRetry(ctx, job, now);
const retry = catalogRetry.kind !== "blocked";
if (catalogRetry.kind === "blocked") {
await terminalizeBlockedCatalogRetry(ctx, catalogRetry, now);
}
await ctx.db.patch(job._id, {
status: "queued",
attempts: Math.max(0, job.attempts - 1),
status: retry ? "queued" : "failed",
attempts: retry ? Math.max(0, job.attempts - 1) : job.attempts,
...(retry
? {}
: {
lastError: "Catalog scan retry blocked by inactive or invalid linkage",
completedAt: now,
}),
leaseToken: undefined,
leaseExpiresAt: undefined,
workerId: undefined,
nextRunAt: now + 60_000,
nextRunAt: retry ? now + 60_000 : job.nextRunAt,
updatedAt: now,
});
if (job.targetKind === "skillScanRequest" && job.skillScanRequestId) {
await ctx.db.patch(job.skillScanRequestId, {
status: "queued",
status: retry ? "queued" : "failed",
...(retry
? {}
: {
lastError: "Catalog scan retry blocked by inactive or invalid linkage",
completedAt: now,
}),
updatedAt: now,
});
}
await requestSecurityScanDispatch(ctx);
if (retry) await requestSecurityScanDispatch(ctx);
return { ok: true as const, nextRunAt: now + 60_000 };
},
});
@@ -2951,7 +3563,13 @@ export const completeCodexScanJob = action({
},
);
if (!target) throw new ConvexError("Job not found");
if (target.job.leaseToken !== args.leaseToken) throw new ConvexError("Lease mismatch");
const isCatalogScanRequest =
target.job.targetKind === "skillScanRequest" &&
target.scanRequest?.sourceKind === "skills-sh-catalog" &&
Boolean(target.scanRequest.skillsShCatalogAttemptId);
if (!isCatalogScanRequest && target.job.leaseToken !== args.leaseToken) {
throw new ConvexError("Lease mismatch");
}
if (target.job.targetKind === "skillVersion" && target.version) {
if (args.skillSpectorAnalysis) {
@@ -3007,6 +3625,36 @@ export const completeCodexScanJob = action({
});
writtenBack = true;
}
if (
target.scanRequest.sourceKind === "skills-sh-catalog" &&
target.scanRequest.skillsShCatalogAttemptId
) {
const result = await runMutationRef<{ ok: true }>(
ctx,
internalRefs.securityScan.completeCatalogSkillScanJobInternal,
{
attemptId: target.scanRequest.skillsShCatalogAttemptId,
scanId: target.scanRequest._id,
jobId: args.jobId,
leaseToken: args.leaseToken,
artifactContentHash: target.scanRequest.sha256hash ?? "",
verdict: githubSkillScanStatusFromLlmAnalysis(args.llmAnalysis),
runId: args.runId,
llmAnalysis: args.llmAnalysis,
skillSpectorAnalysis,
},
);
try {
await runMutationRef(
ctx,
internalRefs.securityScanDispatch.requestSecurityScanDispatchInternal,
{},
);
} catch {
console.warn("security scan dispatch request failed after catalog completion");
}
return result;
}
await runMutationRef(ctx, internalRefs.securityScan.recordSkillScanRequestSucceededInternal, {
scanId: target.scanRequest._id,
jobId: args.jobId,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5
View File
@@ -70,6 +70,8 @@
"seed:test:validate-snapshot": "bun scripts/staging-seed/validate-sanitized-snapshot.ts",
"setup:worktree": "bun scripts/setup-worktree.ts",
"skill-cards:worker": "bun scripts/skill-cards/run-skill-card-worker.ts",
"skills-sh:capture-500": "bun scripts/skills-sh-catalog/capture-frozen-snapshot.ts",
"skills-sh:prove-500": "bun scripts/skills-sh-catalog/prove-500.ts",
"skills:install": "npx --yes skills@1.5.16 add openclaw/design-system --skill openclaw-design openclaw-brand openclaw-design-system openclaw-marketing-pages openclaw-design-audit --agent codex --copy --yes",
"test": "vitest run",
"test:e2e": "vitest run -c vitest.e2e.config.ts",
@@ -111,6 +113,7 @@
"@tanstack/react-router": "1.170.18",
"@tanstack/react-start": "1.168.32",
"@vercel/analytics": "2.0.1",
"@vercel/oidc": "^3.8.0",
"@vercel/speed-insights": "2.0.0",
"class-variance-authority": "0.7.1",
"clawhub-schema": "workspace:0.0.2",
@@ -146,6 +149,7 @@
"zod": "4.4.3"
},
"devDependencies": {
"@edge-runtime/vm": "^5.0.0",
"@faker-js/faker": "^10.5.0",
"@playwright/test": "^1.61.1",
"@react-email/ui": "^6.9.0",
@@ -161,6 +165,7 @@
"@typescript/native": "npm:typescript@7.0.2",
"@vitejs/plugin-react": "6.0.3",
"@vitest/coverage-v8": "4.1.10",
"convex-test": "^0.0.54",
"jsdom": "29.1.1",
"nitro": "3.0.260610-beta",
"only-allow": "1.2.2",
@@ -0,0 +1,292 @@
{
"verdict": "pass",
"generatedAt": "2026-07-21T09:02:16.188Z",
"deployment": {
"kind": "disposable-local-convex",
"name": "local-amantus-clawdhub-7449",
"cloudUrl": "http://127.0.0.1:3210",
"permanentTestDeployed": false,
"productionDeployed": false
},
"runtime": {
"elapsedMs": 42507,
"memory": {
"backendPid": 97459,
"backendRssStartKiB": 816368,
"backendRssPeakKiB": 891456,
"backendRssEndKiB": 863968,
"driverRssStartBytes": 51904512,
"driverRssPeakBytes": 75415552,
"driverRssEndBytes": 75415552,
"driverHeapPeakBytes": 6130176
},
"clientCalls": {
"mutation": 279,
"query": 297,
"expectedErrors": 8,
"unexpectedErrors": 0
},
"sourceFetchesDuringProof": 0,
"frozenSnapshotCaptureFetches": 528
},
"defaultState": {
"controls": {
"discoveryEnabled": false,
"maxCatalogInFlight": 0,
"maxCatalogQueued": 0,
"maxEntriesPerBatch": 0,
"maxEntriesPerRun": 0,
"maxNativeInFlight": 0,
"maxNativeQueued": 0,
"maxPlannedScans": 0,
"maxScanAdmissionsPerBatch": 0,
"maxScanAdmissionsPerDay": 0,
"maxScanAdmissionsPerRun": 0,
"maxWritesPerBatch": 0,
"mode": "off",
"paused": true,
"publicVisibilityEnabled": false,
"realScanAllowlist": [],
"reason": null,
"scanAdmissionEnabled": false,
"scanPlanningEnabled": false,
"updatedAt": null,
"updatedBy": null,
"writesEnabled": false
},
"schedulesPresent": false,
"catalogScanAttempts": 0,
"realScanAttempts": 0
},
"discovery20000": {
"observed": 20000,
"plannedScans": 20000,
"wouldInsert": 20000,
"persistedInserts": 0,
"authoritativePersistedEntries": 0,
"authoritativeScanAttempts": 0,
"authoritativeNativeScanJobs": 0,
"batches": 200,
"operationEstimates": {
"dbReads": 20401,
"dbWrites": 201,
"functionCalls": 201
}
},
"frozen500": {
"capture": {
"snapshotId": "skills-sh-500-2026-07-21",
"capturedAt": "2026-07-21T02:35:28.482Z",
"sourceFetches": 528
},
"configuredBudgets": {
"maxEntriesPerBatch": 125,
"maxEntriesPerRun": 500,
"maxPlannedScans": 500,
"maxScanAdmissionsPerBatch": 49,
"maxScanAdmissionsPerDay": 500,
"maxScanAdmissionsPerRun": 500,
"maxWritesPerBatch": 100
},
"firstRun": {
"expectedActions": {
"wouldInsert": 500,
"wouldUpdate": 0,
"scansPlanned": 500
},
"persistedActions": {
"inserted": 500,
"updated": 0,
"authoritativeEntries": 500,
"authoritativeAttempts": 496,
"completedDeterministicAttempts": 496
},
"counts": {
"inserted": 500,
"observed": 500,
"rejected": 0,
"scansAdmitted": 496,
"scansCanceled": 0,
"scansCompleted": 496,
"scansPlanned": 500,
"unchanged": 0,
"updated": 0,
"wouldInsert": 500,
"wouldUpdate": 0
},
"batches": {
"batchesProcessed": 6,
"entriesObserved": 500,
"lastBatchReads": 7,
"lastBatchWrites": 6,
"scanAdmissionBatches": 11,
"scansAdmitted": 496,
"scansPlanned": 500
},
"operationEstimates": {
"dbReads": 2163,
"dbWrites": 2515,
"functionCalls": 31
}
},
"pauseResume": {
"pausedCursor": 99,
"pausedObserved": 99,
"resumedFinalCursor": 500,
"resumedFinalObserved": 500,
"replayedCompletedBatch": false
},
"identicalRerun": {
"counts": {
"inserted": 0,
"observed": 500,
"rejected": 0,
"scansAdmitted": 0,
"scansCanceled": 0,
"scansCompleted": 0,
"scansPlanned": 0,
"unchanged": 500,
"updated": 0,
"wouldInsert": 0,
"wouldUpdate": 0
},
"authoritativeAttempts": 0,
"operationEstimates": {
"dbReads": 1023,
"dbWrites": 507,
"functionCalls": 7
}
},
"changedRerun": {
"counts": {
"inserted": 0,
"observed": 500,
"rejected": 0,
"scansAdmitted": 1,
"scansCanceled": 0,
"scansCompleted": 1,
"scansPlanned": 1,
"unchanged": 498,
"updated": 2,
"wouldInsert": 0,
"wouldUpdate": 2
},
"authoritativeAttempts": 1,
"exactSourceHashRescan": "665c585455decbcb02116bc928adf5148b9cd0f67b7eb4908c8c0d647c00f680",
"artifactHash": null,
"hashSemantics": "sourceContentHash is the upstream observation; deterministic fixture completion is not an independent ClawScan artifact hash",
"operationEstimates": {
"dbReads": 1040,
"dbWrites": 513,
"functionCalls": 9
}
},
"finalAuthoritativeEntries": 500,
"allEntriesDark": true
},
"concurrency": {
"sameRun": {
"fulfilled": 1,
"rejected": 1,
"cap": 1
},
"queueHealth": {
"queuedAtThresholdRejected": true,
"inFlightAtThresholdRejected": true,
"maxCatalogQueued": 1,
"maxCatalogInFlight": 1,
"maxNativeQueued": 0,
"maxNativeInFlight": 0,
"queuedThresholdObserved": {
"nativeQueued": 0,
"nativeInFlight": 0,
"catalogQueued": 1,
"catalogInFlight": 0
},
"inFlightThresholdObserved": {
"nativeQueued": 0,
"nativeInFlight": 0,
"catalogQueued": 1,
"catalogInFlight": 1
}
},
"crossRunDaily": {
"fulfilled": 1,
"rejected": 1,
"cap": 499,
"attemptsBeforeRace": 498
},
"finalCatalogAttemptsToday": 500
},
"admissionContainment": {
"deterministicOnlyInLocalProof": true,
"realDispatchOutsideStagingLiveRejected": true,
"realDispatchRejection": "[Request ID: add843a2d41d4f84] Server Error\nUncaught ConvexError: real skills.sh scan admission requires staging-live controls\n at handler (../convex/skillsShCatalog.ts:915:14)\n at async handler (../../node_modules/convex-helpers/server/customFunctions.js:268:27)\n"
},
"lifecycleContainment": {
"canceledRunningAttempts": 1,
"canceledQueuedAttempts": 1,
"canceledAttemptReadback": [
{
"dispatchKind": "deterministic",
"status": "canceled"
},
{
"dispatchKind": "deterministic",
"status": "canceled"
}
],
"canceledRunAdmissionRejected": true,
"canceledRunAdmissionRejection": "[Request ID: 286aefc65cc575ac] Server Error\nUncaught ConvexError: Cannot admit scans for canceled run\n at handler (../convex/skillsShCatalog.ts:902:24)\n at async handler (../../node_modules/convex-helpers/server/customFunctions.js:268:27)\n",
"killSwitchAdmissionRejected": true,
"killSwitchAdmissionRejection": "[Request ID: b4c1e019ae188e8a] Server Error\nUncaught ConvexError: skills.sh catalog controls are disabled\n at assertCatalogActive (../convex/skillsShCatalog.ts:157:14)\n at assertScanAdmissionEnabled (../convex/skillsShCatalog.ts:179:14)\n at handler (../convex/skillsShCatalog.ts:893:24)\n at async handler (../../node_modules/convex-helpers/server/customFunctions.js:268:27)\n"
},
"rollback": {
"controls": {
"discoveryEnabled": false,
"maxCatalogInFlight": 10,
"maxCatalogQueued": 50,
"maxEntriesPerBatch": 125,
"maxEntriesPerRun": 500,
"maxNativeInFlight": 0,
"maxNativeQueued": 0,
"maxPlannedScans": 500,
"maxScanAdmissionsPerBatch": 49,
"maxScanAdmissionsPerDay": 500,
"maxScanAdmissionsPerRun": 500,
"maxWritesPerBatch": 100,
"mode": "off",
"paused": true,
"publicVisibilityEnabled": false,
"realScanAllowlist": [],
"reason": "visibility and control rollback proof",
"scanAdmissionEnabled": false,
"scanPlanningEnabled": false,
"updatedAt": 1784624535953,
"updatedBy": "claw-556-local-proof",
"writesEnabled": false
},
"schedulesPresent": false,
"nativeBefore": {
"skills": 0,
"scanJobs": 0,
"sha256": "3da07503745b0261278284c398b652c73edeb004df305c460686e5b0b12c05b0"
},
"nativeAfter": {
"skills": 0,
"scanJobs": 0,
"sha256": "3da07503745b0261278284c398b652c73edeb004df305c460686e5b0b12c05b0"
},
"nativeStateUnchanged": true
},
"limits": {
"operationCountsAreEstimates": true,
"realTestQueueIntegration": "not deployed; real attempts require an allowlisted Test control and an external artifact fetch before completion",
"statusPreviewLimit": {
"entries": 50,
"runs": 20,
"scanAttempts": 50
},
"authoritativeReadback": "all entry, attempt, native skill, and native scan-job counts above use paginated internal queries"
}
}
@@ -0,0 +1,376 @@
{
"schemaVersion": 1,
"issue": "CLAW-556",
"generatedAt": "2026-07-21T14:23:00Z",
"question": "Does the permanent Test staging seam fetch and process exactly 500 live skills through the ClawHub Vercel OIDC boundary without broad scan admission, public visibility, scheduling, or native-state changes?",
"verdict": "pass_for_review",
"sourceRevision": {
"gitSha": "06e8ff1a2b71f9d544623f85c4284446ad7878de",
"branch": "pe/claw-556-skills-sh-catalog-control-plane",
"convexDeployment": "academic-chihuahua-392",
"convexBuildSha": "06e8ff1a2b71f9d544623f85c4284446ad7878de",
"convexReadbackAt": "2026-07-21T14:09:48Z",
"liveProofInvocationSha": "f74f895a0c0fe13c3a49de7f9ff0a9e6a9df0446",
"hardeningScopeAfterLiveProof": "Artifact byte/hash validation, authoritative staging identity checks, direct real-dispatch rejection, admin authorization, missing-run callback rejection, source-kind collision rejection, allowlist normalization, terminal retry synchronization, strict fixture-versus-staging-live mode isolation, bounded multi-file write accounting, backlog draining, and terminal catalog-attempt synchronization. Discovery, normalization, persistence batching, cursoring, and the 500-row source contract were unchanged."
},
"vercel": {
"project": "openclaw-foundation/clawhub",
"verifiedOidcIdentity": {
"ownerId": "team_pLdjXbfy0XvPRiNmAygTjTSH",
"projectId": "prj_UVAJPNPYrBwTEkPJwkpEySsge8Mc",
"environment": "test"
},
"liveInvocation": {
"deploymentId": "dpl_9ndfyttJWaTC3LZibUgL371TstFx",
"url": "clawhub-nh4plivpm-openclaw-foundation.vercel.app",
"target": "test",
"sourceSha": "f74f895a0c0fe13c3a49de7f9ff0a9e6a9df0446",
"proofCompleted": true,
"deploymentState": "ERROR",
"deploymentLimit": "The one-shot proof build intentionally did not emit the normal application output after the bounded invocation, so Vercel marked that invocation deployment as ERROR after the proof response was captured."
},
"finalInertDeployment": {
"deploymentId": "dpl_FvMrDG1W3PCDs2v5PggkF4Bnt7sV",
"url": "clawhub-368srhyyu-openclaw-foundation.vercel.app",
"target": "test",
"sourceSha": "06e8ff1a2b71f9d544623f85c4284446ad7878de",
"readyState": "READY",
"createdAt": "2026-07-21T14:20:41.046Z",
"runtime": "nodejs24.x",
"memorySizeMiB": 2048,
"timeoutSeconds": 300,
"liveInvocationMarkerPresent": false,
"externalProtectedStatuses": {
"/ops/skills-sh/catalog-test": 302,
"/api/v1/operator/skills-sh/catalog-test": 302
},
"deploymentProtectionEnabled": true,
"newCatalogRunsAfterDeploy": 0,
"sourceBinding": {
"gitCommitSha": "06e8ff1a2b71f9d544623f85c4284446ad7878de",
"purpose": "clean inert exact-head deployment"
}
}
},
"liveSnapshot": {
"snapshotId": "skills-sh-test-live-500:5dbb0caa415371e5",
"capturedAt": "2026-07-21T09:55:56.277Z",
"rows": 500,
"nvidiaRows": 10,
"requiredCollisionIds": [
"anthropics/skills/frontend-design",
"anthropics/claude-code/frontend-design"
],
"skillsShFetches": {
"total": 507,
"list": 2,
"search": 1,
"detail": 504,
"runtimeMs": 38317,
"skippedIncompleteDetails": 1
},
"githubOwnerResolution": {
"authenticatedFetches": 70,
"immutableOwnerIdsRequiredForEverySelectedRow": true,
"placeholderOwnerIdsAccepted": false
}
},
"configuredLimits": {
"discoveryRows": 500,
"entriesPerBatch": 50,
"entriesPerRun": 500,
"writesPerBatch": 100,
"plannedScansPerRun": 500,
"explicitRealScanAllowlistCeiling": 10,
"realScanAdmissionsPerBatch": 1,
"realScanAdmissionsPerRun": 1,
"realScanAdmissionsPerDay": 1,
"nativeQueuedThreshold": 3,
"nativeInFlightThreshold": 0,
"catalogQueuedThreshold": 1,
"catalogInFlightThreshold": 1,
"schedulesEnabled": false,
"publicVisibilityEnabled": false
},
"liveRun": {
"runId": "h17w3pzcbww82myvmz8a1msnxh8azbss",
"status": "completed",
"cursor": 500,
"runtimeMs": 16148,
"batchesProcessed": 10,
"expectedActions": {
"observed": 500,
"inserts": 0,
"updates": 0,
"unchanged": 500,
"scanPlans": 0,
"realScanAdmissions": 0
},
"persistedActions": {
"catalogEntries": 500,
"inserted": 0,
"updated": 0,
"unchanged": 500,
"scanAttempts": 0,
"realScanJobs": 0
},
"consumedBudgets": {
"entriesObserved": 500,
"batchesProcessed": 10,
"scansPlanned": 0,
"scanAdmissionBatches": 0,
"scansAdmitted": 0,
"lastBatchReadsEstimate": 102,
"lastBatchWritesEstimate": 51
},
"operationEstimates": {
"dbReads": 1021,
"dbWrites": 511,
"functionCalls": 11
},
"errors": []
},
"identicalRerun": {
"runId": "h17qgyp7yhwt5g4dvfyppdw0gd8ayt02",
"status": "completed",
"cursor": 500,
"runtimeMs": 15979,
"batchesProcessed": 10,
"observed": 500,
"unchanged": 500,
"inserted": 0,
"updated": 0,
"scanPlans": 0,
"scanAttempts": 0,
"realScanJobs": 0,
"errors": []
},
"changedInputRun": {
"runId": "h17n18bmyq5tr5mdka784mm0dn8aycxe",
"status": "completed",
"runtimeMs": 16635,
"observed": 500,
"persistedMetadataUpdates": 453,
"unchanged": 47,
"changedSourceHashes": 3,
"scanPlans": 3,
"scanAttempts": 0,
"realScanJobs": 0,
"changedExternalIds": [
"alchaincyf/huashu-design/huashu-design",
"getpaperclipai/paperclip/paperclip-converting-plans-to-tasks",
"getpaperclipai/paperclip/para-memory-files"
],
"errors": []
},
"pauseCancelKillSwitch": {
"runId": "h17thn76pzx9phr0w0g9rxgrw58aznq9",
"cursorAfterFirstBatch": 50,
"pausedBatchRejected": true,
"cursorAfterResumeBatch": 100,
"batchesAfterResume": 2,
"completedBatchReplayObserved": false,
"finalStatus": "canceled",
"attemptsCanceled": 0,
"discoveryRejectedAfterKillSwitch": true,
"admissionRejectedAfterKillSwitch": true
},
"admission": {
"requested": 0,
"admitted": 0,
"skipped": 0,
"realQueueAfterRun": 0,
"catalogAttemptsAfterRun": 0,
"nativeQueueBefore": {
"queued": 3,
"inFlight": 0
},
"nativeQueueAfter": {
"queued": 3,
"inFlight": 0
},
"reasonForZeroAdmissions": "The selected NVIDIA and representative collision candidates exposed incomplete source file bodies. The real path requires a complete fetched artifact and exact artifact hash, so admission failed closed rather than weakening validation."
},
"runtime": {
"totalElapsedMs": 75008,
"rssStartBytes": 160522240,
"rssEndBytes": 269778944,
"rssDeltaBytes": 109256704,
"heapUsedStartBytes": 19133656,
"heapUsedEndBytes": 98823704,
"heapUsedDeltaBytes": 79690048,
"sourceFetches": 507,
"githubFetches": 70,
"errors": 0,
"operationCountsAreEstimates": true
},
"authoritativeReadback": {
"nativeSkills": {
"beforeCount": 67616,
"afterCount": 67616,
"beforeSha256": "267d17957e61a49f37dd009a5d88194b3587daf86281e253522ff1b7c9ad80f7",
"afterSha256": "267d17957e61a49f37dd009a5d88194b3587daf86281e253522ff1b7c9ad80f7",
"identical": true,
"pagesReadAfter": 68
},
"nativeSecurityScanJobs": {
"beforeCount": 3,
"afterCount": 3,
"beforeSha256": "3c08230f417303a66c0678e7c6192864d045bca72581ab1b51a9908c64f36ddc",
"afterSha256": "3c08230f417303a66c0678e7c6192864d045bca72581ab1b51a9908c64f36ddc",
"identical": true,
"sources": {
"publish": 3
},
"statuses": {
"queued": 3
}
},
"catalog": {
"entries": 500,
"entriesPublicVisible": 0,
"entriesInstallable": 0,
"scanStatusPlanned": 500,
"scanAttempts": 0,
"realScanQueue": 0
}
},
"finalExactHeadReadback": {
"capturedAt": "2026-07-21T14:22:02Z",
"convexDeployment": "academic-chihuahua-392",
"convexBuildSha": "06e8ff1a2b71f9d544623f85c4284446ad7878de",
"vercelDeploymentId": "dpl_FvMrDG1W3PCDs2v5PggkF4Bnt7sV",
"vercelSourceSha": "06e8ff1a2b71f9d544623f85c4284446ad7878de",
"latestCatalogRunUpdatedAt": 1784627791018,
"latestCatalogRunPredatesFinalDeploy": true,
"control": {
"mode": "off",
"paused": true,
"discoveryEnabled": false,
"writesEnabled": false,
"scanPlanningEnabled": false,
"scanAdmissionEnabled": false,
"publicVisibilityEnabled": false,
"realScanAllowlist": []
},
"nativeSkills": {
"count": 67616,
"pagesRead": 68,
"pageJsonSha256": "987ae4e1dd9bb5539b7febd1223f6a3eb8080cdbad759d3d7033cbe54f6d42f1"
},
"nativeSecurityScanJobs": {
"count": 3,
"pagesRead": 1,
"cliJsonSha256": "e22b14a16593fb8be93959a91161da2a51f6fa52c62c570fea4425e5641b0759",
"sources": {
"publish": 3
},
"statuses": {
"queued": 3
},
"catalogAttributed": 0
},
"catalog": {
"entries": 500,
"entryPagesRead": 1,
"entriesCliJsonSha256": "eede6eb7beff0ce4959b38b2e64faf55c44dcff1f27a9ed5ba36affc89afba66",
"sourceKinds": {
"staging-live": 500
},
"scanStatuses": {
"planned": 500
},
"entriesPublicVisible": 0,
"entriesInstallable": 0,
"scanAttempts": 0,
"attemptPagesRead": 1,
"attemptsCliJsonSha256": "2e2fdeefcc79db019282c22b2db56c47c9019a8b47a40a9083c53746c92f7caa",
"realScanAttempts": 0
},
"hashNote": "These fresh hashes cover complete paginated Test readback at exact head 06e8ff1a. They are serialization-specific snapshots and are not compared numerically with the in-run ConvexHttpClient hashes; authoritative counts, source/status distributions, and the unchanged latest run timestamp establish that the inert deployments created no catalog or native work."
},
"finalControls": {
"mode": "off",
"paused": true,
"discoveryEnabled": false,
"writesEnabled": false,
"scanPlanningEnabled": false,
"scanAdmissionEnabled": false,
"publicVisibilityEnabled": false,
"realScanAllowlist": [],
"schedulesPresent": false,
"githubTokenPresentInConvex": false,
"temporaryOperatorTokensRevoked": true,
"temporaryInvocationFilesRemoved": true,
"productionTouched": false
},
"alternateAuthenticatedRetry": {
"attemptedAt": "2026-07-21T14:11:43Z",
"requiredAnotherLiveFetch": false,
"sourceFetches": 0,
"catalogRunsCreated": 0,
"scanAttemptsCreated": 0,
"productionTouched": false,
"diagnosis": [
"The protected Test route could not be reached through vercel curl because the project automation bypass secret is intentionally unavailable locally.",
"Two build-time OIDC retries failed before source authorization: one referenced the wrong token function name and one lacked the explicit Test build marker.",
"A third deployment restored and ran the normal build command, so it produced no source fetch or catalog mutation."
],
"cleanup": {
"controlMode": "off",
"paused": true,
"discoveryEnabled": false,
"writesEnabled": false,
"scanPlanningEnabled": false,
"scanAdmissionEnabled": false,
"publicVisibilityEnabled": false,
"realScanAllowlist": [],
"temporaryOperatorTokenRevoked": true,
"temporaryInvocationFilePresent": false,
"normalBuildCommandRestored": true
}
},
"overloadAcceptance": {
"local20000RowProofArtifact": "proof/claw-556/skills-sh-500-local-proof.json",
"discovery20000RowsCreatesAutomaticNativeJobs": 0,
"discovery20000RowsCreatesCatalogAttempts": 0,
"permanentTestFetchRows": 500,
"permanentTestRealScanCeiling": 10,
"permanentTestRealScansObserved": 0,
"discoveryWritesAdmissionVisibilityIndependentlyControlled": true,
"unhealthyOrExhaustedAdmissionFailsClosed": true,
"dailySchedulingDefaultOff": true
},
"hardeningValidation": {
"focusedTests": {
"files": 6,
"testsPassed": 152
},
"convexCodegenWithTypeScript": "passed",
"ciStatic": "passed",
"ciUnit": {
"filesPassed": 382,
"filesSkipped": 1,
"testsPassed": 4949,
"testsSkipped": 1,
"statementsPercent": 85.89,
"branchesPercent": 76.71,
"functionsPercent": 86.95,
"linesPercent": 88.59
},
"ciTypesBuild": "passed",
"structuredAutoreview": {
"branchFindingAccepted": "Fixture work could advance under staging-live controls.",
"fix": "Fixture start, processing, deterministic admission, start, completion, and callback now require fixture mode and a fixture run.",
"finalLocalReview": "clean after final exact-head readback correction"
},
"liveFetchRepeatedAfterHardening": false,
"reasonLiveFetchWasNotRepeated": "The post-proof changes only tightened staging admission, artifact validation, environment authorization, callback/retry synchronization, source-kind collision handling, fixture-versus-live mode isolation, bounded write accounting, and backlog draining. The exact 500-row source and discovery path was unchanged; exact head 06e8ff1a was deployed inert to both permanent Test runtimes and read back with zero new runs, attempts, or jobs."
},
"limits": [
"The skills.sh detail contract did not provide complete file bodies for the selected real-scan candidates, so this gate proves fail-closed artifact admission rather than a terminal real ClawScan verdict.",
"The live 500-row invocation ran at f74f895a; final hardened descendant 06e8ff1a was deployed to both exact Test runtimes with the live-fetch marker absent. The proof records this split explicitly rather than claiming a second live fetch.",
"Vercel local-upload deployments do not expose a gitSource object. The source SHA is bound by the clean detached worktree, the fixed one-shot invoker assertion, the Convex APP_BUILD_SHA readback, and the final conventional commit chain.",
"Database operation counts are code-path estimates. Runtime, memory, fetch counts, full paginated table counts, and canonical SHA-256 comparisons are measured."
]
}
@@ -1,5 +1,6 @@
/* @vitest-environment node */
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
@@ -10,6 +11,7 @@ import { processJob } from "./run-codex-scan-worker";
const tempDirs: string[] = [];
const execFileAsync = promisify(execFile);
const sha256 = (content: string) => createHash("sha256").update(content).digest("hex");
afterEach(async () => {
vi.restoreAllMocks();
@@ -45,7 +47,7 @@ function skillVersionJob(jobId: string): ClaimedJob {
files: [
{
path: "SKILL.md",
sha256: "abc123",
sha256: sha256("# Skill"),
size: 42,
url: "data:text/plain,%23%20Skill",
},
@@ -86,7 +88,7 @@ function fileTarget(path: string, content: string): ClaimedJob["target"] {
files: [
{
path,
sha256: "artifact-sha",
sha256: sha256(content),
size: Buffer.byteLength(content),
url: `data:text/plain,${encodeURIComponent(content)}`,
},
+37 -2
View File
@@ -385,7 +385,7 @@ describe("run-codex-scan-worker diagnostics", () => {
files: [
{
path: "SKILL.md",
sha256: "abc123",
sha256: "e2151f8490121dc5e6fd36c1d4e00b6da5593595e3eb8ece76c1d0ec3f310979",
size: 42,
url: "data:text/plain,%23%20Skill",
},
@@ -409,7 +409,42 @@ describe("run-codex-scan-worker diagnostics", () => {
source: "publish",
targetKind: "skillVersion",
});
expect(metadata.target.files).toEqual([{ path: "SKILL.md", sha256: "abc123", size: 42 }]);
expect(metadata.target.files).toEqual([
{
path: "SKILL.md",
sha256: "e2151f8490121dc5e6fd36c1d4e00b6da5593595e3eb8ece76c1d0ec3f310979",
size: 42,
},
]);
});
it("rejects downloaded bytes that do not match the stored file hash", async () => {
const workspace = await tempDir();
await expect(
writeArtifactWorkspace(
{
job: {
_id: "catalog-job",
hasMaliciousSignal: false,
leaseToken: "test-auth-token",
source: "skills-sh-catalog-test",
targetKind: "skillScanRequest",
waitForVtUntil: 0,
},
target: {
files: [
{
path: "SKILL.md",
sha256: "0".repeat(64),
size: 7,
url: "data:text/plain,%23%20Skill",
},
],
},
},
workspace,
),
).rejects.toThrow("Downloaded artifact hash mismatch for artifact file SKILL.md");
});
it("materializes zero-byte directory markers with descendant files", async () => {
+11 -2
View File
@@ -1,4 +1,5 @@
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdirSync, readFileSync } from "node:fs";
import { appendFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
@@ -237,7 +238,8 @@ function parseArgs() {
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
};
const laneValue = get("--lane") ?? process.env.CODEX_SECURITY_SCAN_LANE;
const lane: "priority" | "shared" = laneValue === "priority" ? "priority" : "shared";
const lane: "priority" | "shared" | "catalog" =
laneValue === "priority" || laneValue === "catalog" ? laneValue : "shared";
return {
batchLimit: numberFrom(
get("--batch-limit") ?? get("--limit") ?? process.env.CODEX_SECURITY_SCAN_LIMIT,
@@ -757,7 +759,14 @@ export async function writeArtifactWorkspace(job: ClaimedJob, workspace: string)
const { file, out } = candidate;
await mkdir(dirname(out), { recursive: true });
await writeFile(out, await download(file.url, { kind: "file", path: file.path }));
const bytes = await download(file.url, { kind: "file", path: file.path });
const actualSha256 = createHash("sha256").update(bytes).digest("hex");
if (actualSha256 !== file.sha256.toLowerCase()) {
throw new Error(
`Downloaded artifact hash mismatch for artifact file ${safeWorkerArtifactPathLabel(file.path)}`,
);
}
await writeFile(out, bytes);
}
if (job.target.clawpackUrl) {
@@ -0,0 +1,215 @@
#!/usr/bin/env bun
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import {
fetchSkillsShCatalogDetail,
fetchSkillsShCatalogPage,
searchSkillsShCatalog,
type SkillsShCatalogListRow,
} from "../../server/skillsShCatalogSource";
import { resolveAuthenticatedGitHubOwner } from "./github-owner-resolution";
const SNAPSHOT_ID = "skills-sh-500-2026-07-21";
const ROW_LIMIT = 500;
const DETAIL_CANDIDATE_LIMIT = 525;
const DETAIL_CONCURRENCY = 8;
const OUTPUT_PATH = resolve("convex/fixtures/skills-sh-500-2026-07-21.json");
const REQUIRED_COLLISION_IDS = [
"anthropics/skills/frontend-design",
"anthropics/claude-code/frontend-design",
] as const;
type FrozenRow = {
externalId: string;
githubOwnerId: number;
owner: string;
repo: string;
slug: string;
displayName: string;
sourceUrl: string;
githubRepoUrl: string;
sourceContentHash: string;
installs: number;
};
function isGitHubRow(row: SkillsShCatalogListRow) {
const normalizedSource = row.source.trim().toLowerCase();
const normalizedSlug = row.slug.trim().toLowerCase();
return (
row.sourceType === "github" &&
normalizedSource.split("/").length === 2 &&
/^[a-z0-9][a-z0-9-]*$/.test(normalizedSlug) &&
row.id.trim().toLowerCase() === `${normalizedSource}/${normalizedSlug}`
);
}
function normalizeRow(row: SkillsShCatalogListRow) {
const [owner = "", repo = ""] = row.source.split("/");
return {
...row,
id: `${owner.trim().toLowerCase()}/${repo.trim().toLowerCase()}/${row.slug
.trim()
.toLowerCase()}`,
owner: owner.trim().toLowerCase(),
repo: repo.trim().toLowerCase(),
slug: row.slug.trim().toLowerCase(),
};
}
async function mapWithConcurrency<T, R>(
values: readonly T[],
concurrency: number,
mapper: (value: T, index: number) => Promise<R>,
) {
const results = Array.from<R>({ length: values.length });
let nextIndex = 0;
async function worker() {
while (nextIndex < values.length) {
const index = nextIndex;
nextIndex += 1;
results[index] = await mapper(values[index] as T, index);
}
}
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, () => worker()));
return results;
}
async function main() {
const startedAt = Date.now();
const candidates = new Map<string, ReturnType<typeof normalizeRow>>();
let listFetches = 0;
for (let page = 0; candidates.size < ROW_LIMIT + 100; page += 1) {
const response = await fetchSkillsShCatalogPage({ page, perPage: ROW_LIMIT });
listFetches += 1;
for (const row of response.data) {
if (!isGitHubRow(row)) continue;
const normalized = normalizeRow(row);
candidates.set(normalized.id, normalized);
}
if (!response.pagination.hasMore) break;
}
const nvidia = await searchSkillsShCatalog({
query: "nvidia",
owner: "nvidia",
limit: 200,
});
const nvidiaRows = nvidia.data
.filter(isGitHubRow)
.map(normalizeRow)
.filter((row) => row.owner === "nvidia")
.slice(0, 10);
const requiredIds = new Set<string>([
...REQUIRED_COLLISION_IDS,
...nvidiaRows.map((row) => row.id),
]);
for (const row of nvidiaRows) candidates.set(row.id, row);
for (const id of requiredIds) {
if (!candidates.has(id)) {
throw new Error(`Required skills.sh fixture row is missing: ${id}`);
}
}
const detailCandidates = [
...Array.from(requiredIds, (id) => candidates.get(id)!),
...Array.from(candidates.values()).filter((row) => !requiredIds.has(row.id)),
].slice(0, DETAIL_CANDIDATE_LIMIT);
if (detailCandidates.length !== DETAIL_CANDIDATE_LIMIT) {
throw new Error(
`Expected ${DETAIL_CANDIDATE_LIMIT} detail candidates, received ${detailCandidates.length}`,
);
}
const details = await mapWithConcurrency(
detailCandidates,
DETAIL_CONCURRENCY,
async (row) => await fetchSkillsShCatalogDetail(row.id),
);
const selected = detailCandidates
.map((row, index) => ({ row, detail: details[index] }))
.filter(
(
candidate,
): candidate is {
row: ReturnType<typeof normalizeRow>;
detail: NonNullable<(typeof details)[number]> & { hash: string };
} =>
candidate.detail !== null &&
candidate.detail.id.toLowerCase() === candidate.row.id &&
typeof candidate.detail.hash === "string" &&
/^[a-f0-9]{64}$/i.test(candidate.detail.hash),
)
.slice(0, ROW_LIMIT);
if (selected.length !== ROW_LIMIT) {
throw new Error(`Only ${selected.length} rows had canonical ids and exact content hashes`);
}
for (const id of requiredIds) {
if (!selected.some((candidate) => candidate.row.id === id)) {
throw new Error(`Required skills.sh fixture row lacks an exact detail hash: ${id}`);
}
}
const owners = Array.from(new Set(selected.map(({ row }) => row.owner))).sort();
const ownerIds = new Map(
owners.map((owner) => {
const resolved = resolveAuthenticatedGitHubOwner(owner);
return [owner, resolved.id] as const;
}),
);
const rows: FrozenRow[] = selected.map(({ row, detail }) => {
return {
externalId: row.id,
githubOwnerId: ownerIds.get(row.owner)!,
owner: row.owner,
repo: row.repo,
slug: row.slug,
displayName: row.name.trim() || row.slug,
sourceUrl: row.url,
githubRepoUrl: row.installUrl ?? `https://github.com/${row.owner}/${row.repo}`,
sourceContentHash: detail.hash.toLowerCase(),
installs: row.installs,
};
});
const capturedAt = new Date().toISOString();
const payload = {
snapshotId: SNAPSHOT_ID,
capturedAt,
source: {
project: "openclaw-foundation/clawhub",
endpoint: "https://skills.sh/api/v1/skills",
authentication: "vercel-oidc",
requestedRows: ROW_LIMIT,
},
selection: {
rows: rows.length,
nvidiaRows: rows.filter((row) => row.owner === "nvidia").length,
requiredCollisionIds: REQUIRED_COLLISION_IDS,
},
captureMetrics: {
runtimeMs: Date.now() - startedAt,
skillsShFetches: listFetches + 1 + details.length,
githubOwnerFetches: owners.length,
listFetches,
searchFetches: 1,
detailFetches: details.length,
},
rows,
};
await mkdir(dirname(OUTPUT_PATH), { recursive: true });
await writeFile(OUTPUT_PATH, `${JSON.stringify(payload, null, 2)}\n`);
console.log(
JSON.stringify({
outputPath: OUTPUT_PATH,
snapshotId: SNAPSHOT_ID,
rows: rows.length,
nvidiaRows: payload.selection.nvidiaRows,
collisionIds: REQUIRED_COLLISION_IDS,
captureMetrics: payload.captureMetrics,
}),
);
}
await main();
@@ -0,0 +1,20 @@
import { spawnSync } from "node:child_process";
export function resolveAuthenticatedGitHubOwner(ownerInput: string) {
const owner = ownerInput.trim().toLowerCase();
if (!owner) throw new Error("GitHub owner is required");
const result = spawnSync("gh", ["api", `users/${owner}`, "--jq", "{id,login}"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
if (result.status !== 0) {
throw new Error(`Authenticated GitHub owner lookup failed: ${owner}`);
}
const payload = JSON.parse(result.stdout) as { id?: unknown; login?: unknown };
const id = typeof payload.id === "number" ? payload.id : Number.NaN;
const login = typeof payload.login === "string" ? payload.login.trim().toLowerCase() : "";
if (!Number.isSafeInteger(id) || id <= 0 || login !== owner) {
throw new Error(`Authenticated GitHub owner lookup returned invalid identity: ${owner}`);
}
return { owner, id, login };
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
#!/usr/bin/env bun
function requireEnv(name: string) {
const value = process.env[name]?.trim();
if (!value) throw new Error(`${name} is required`);
return value;
}
const targetUrl = requireEnv("CLAWHUB_TEST_CATALOG_GATE_URL");
const operatorAuthorization = requireEnv("CLAWHUB_TEST_OPERATOR_TOKEN");
const allowlist = (process.env.CLAWHUB_TEST_CATALOG_ALLOWLIST ?? "")
.split(",")
.map((externalId) => externalId.trim().toLowerCase())
.filter(Boolean);
if (allowlist.length > 10) throw new Error("CLAWHUB_TEST_CATALOG_ALLOWLIST cannot exceed 10");
async function callGate(body: Record<string, unknown>) {
const response = await fetch(targetUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${operatorAuthorization}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return { response, text: await response.text() };
}
const request = {
allowlist,
reason: "CLAW-556 bounded permanent Test proof",
};
const execution = await callGate(request);
if (!execution.response.ok) {
throw new Error(
`Permanent Test catalog gate returned HTTP ${execution.response.status}: ${execution.text}`,
);
}
console.log(JSON.stringify(JSON.parse(execution.text) as Record<string, unknown>));
export {};
@@ -0,0 +1,248 @@
import { getVercelOidcToken } from "@vercel/oidc";
import { defineEventHandler, getHeader, readBody } from "h3";
import {
captureSkillsShCatalogTestSnapshot,
getSkillsShCatalogTestSourcePolicy,
type SkillsShCatalogGitHubOwnerProof,
} from "../../../skillsShCatalogSource";
const TEST_CONVEX_SITE_URL = "https://academic-chihuahua-392.convex.site";
const OPERATOR_PATH = "/api/v1/operator/skills-sh/catalog-test";
const MAX_BATCH_SIZE = 50;
type CatalogTestRequest = {
allowlist?: string[];
reason?: string;
};
function parseCatalogTestRequest(value: unknown): CatalogTestRequest | null {
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
const body = value as Record<string, unknown>;
if (
(body.allowlist !== undefined &&
(!Array.isArray(body.allowlist) ||
!body.allowlist.every((externalId) => typeof externalId === "string"))) ||
(body.reason !== undefined && typeof body.reason !== "string")
) {
return null;
}
return {
...(body.allowlist !== undefined ? { allowlist: body.allowlist as string[] } : {}),
...(body.reason !== undefined ? { reason: body.reason as string } : {}),
};
}
function jsonResponse(payload: unknown, status = 200) {
return new Response(JSON.stringify(payload), {
status,
headers: {
"Cache-Control": "no-store",
"Content-Type": "application/json; charset=utf-8",
},
});
}
async function callConvexOperator(
authorization: string,
init: { method: "GET" } | { method: "POST"; body: Record<string, unknown> },
) {
const response = await fetch(`${TEST_CONVEX_SITE_URL}${OPERATOR_PATH}`, {
method: init.method,
headers: {
Accept: "application/json",
Authorization: authorization,
...(init.method === "POST" ? { "Content-Type": "application/json" } : {}),
},
...(init.method === "POST" ? { body: JSON.stringify(init.body) } : {}),
});
const text = await response.text();
if (!response.ok) {
throw new Error(`Convex Test operator returned HTTP ${response.status}: ${text.slice(0, 300)}`);
}
return JSON.parse(text) as Record<string, unknown>;
}
async function executeSnapshotRun(
authorization: string,
snapshot: {
snapshotId: string;
capturedAt: string;
rows: Array<Record<string, unknown>>;
metrics: { skillsShFetches: number };
},
reason: string,
batchSize: number,
) {
const start = await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "start",
reason,
snapshotId: snapshot.snapshotId,
sourceCapturedAt: snapshot.capturedAt,
snapshotCaptureFetches: snapshot.metrics.skillsShFetches,
fixtureLength: snapshot.rows.length,
},
});
const runId = start.runId;
if (typeof runId !== "string") throw new Error("Convex Test operator did not return a run id");
let run: Record<string, unknown> | null = null;
for (let cursor = 0; cursor < snapshot.rows.length; cursor += batchSize) {
run = await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "batch",
runId,
cursor,
rows: snapshot.rows.slice(cursor, cursor + batchSize),
},
});
}
if (run?.status !== "completed" || run.cursor !== snapshot.rows.length) {
throw new Error(`Convex Test snapshot run did not complete all ${snapshot.rows.length} rows`);
}
return { runId, run };
}
function batchSizeFromControl(control: Record<string, unknown>) {
const maxEntriesPerBatch = control.maxEntriesPerBatch;
const maxWritesPerBatch = control.maxWritesPerBatch;
if (
typeof maxEntriesPerBatch !== "number" ||
!Number.isInteger(maxEntriesPerBatch) ||
maxEntriesPerBatch < 1 ||
typeof maxWritesPerBatch !== "number" ||
!Number.isInteger(maxWritesPerBatch) ||
maxWritesPerBatch < 2
) {
throw new Error("Convex Test controls do not provide usable batch budgets");
}
return Math.min(MAX_BATCH_SIZE, maxEntriesPerBatch, Math.floor(maxWritesPerBatch / 2));
}
export default defineEventHandler(async (event) => {
const policy = getSkillsShCatalogTestSourcePolicy(process.env);
if (!policy.allowed) return jsonResponse({ error: "not_found" }, 404);
const authorization = getHeader(event, "authorization")?.trim() ?? "";
if (!authorization.toLowerCase().startsWith("bearer ")) {
return jsonResponse({ error: "operator_authorization_required" }, 401);
}
let rawBody: unknown;
try {
rawBody = await readBody(event);
} catch {
return jsonResponse({ error: "invalid_request_body" }, 400);
}
const body = parseCatalogTestRequest(rawBody);
if (!body) return jsonResponse({ error: "invalid_request_body" }, 400);
const allowlist = Array.from(
new Set((body.allowlist ?? []).map((externalId) => externalId.trim().toLowerCase())),
).filter(Boolean);
if (allowlist.length > policy.maxRealScanAdmissions) {
return jsonResponse({ error: "allowlist_exceeds_test_ceiling" }, 400);
}
const memoryStart = process.memoryUsage();
const startedAt = Date.now();
try {
const staging = await callConvexOperator(authorization, { method: "GET" });
const control = staging.control;
if (!control || typeof control !== "object") {
throw new Error("Convex Test operator did not return catalog controls");
}
const batchSize = batchSizeFromControl(control as Record<string, unknown>);
const snapshot = await captureSkillsShCatalogTestSnapshot({
env: process.env,
async getOidcToken() {
return await getVercelOidcToken();
},
readConvexControl: async () =>
control as {
mode: "off" | "fixture" | "staging-live";
discoveryEnabled: boolean;
writesEnabled: boolean;
scanPlanningEnabled: boolean;
maxEntriesPerRun: number;
publicVisibilityEnabled: boolean;
},
admitExternalIds: allowlist,
resolveGitHubOwners: async (owners) =>
(await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "resolve-owners",
owners,
},
})) as SkillsShCatalogGitHubOwnerProof,
});
const reason = body.reason?.trim() || "bounded permanent Test skills.sh proof";
const firstRun = await executeSnapshotRun(authorization, snapshot, reason, batchSize);
const identicalRerun = await executeSnapshotRun(
authorization,
snapshot,
`${reason} identical rerun`,
batchSize,
);
const admission =
allowlist.length > 0
? await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "admit",
runId: firstRun.runId,
externalIds: allowlist,
artifacts: snapshot.artifacts,
},
})
: { requested: 0, admitted: 0, skipped: 0 };
if (
admission.requested !== allowlist.length ||
admission.admitted !== allowlist.length ||
admission.skipped !== 0
) {
throw new Error("Convex Test did not admit the complete allowlist");
}
const memoryEnd = process.memoryUsage();
return jsonResponse({
ok: true,
source: {
project: "openclaw-foundation/clawhub",
vercelSourceSha: process.env.VERCEL_GIT_COMMIT_SHA ?? null,
vercelTargetEnvironment: process.env.VERCEL_TARGET_ENV ?? null,
verifiedIdentity: snapshot.verifiedIdentity,
snapshotId: snapshot.snapshotId,
capturedAt: snapshot.capturedAt,
selection: snapshot.selection,
fetches: snapshot.metrics,
},
convex: {
deploymentName: staging.deploymentName,
buildSha: staging.buildSha,
firstRun,
identicalRerun,
admission,
},
runtime: {
elapsedMs: Date.now() - startedAt,
rssStartBytes: memoryStart.rss,
rssEndBytes: memoryEnd.rss,
heapUsedStartBytes: memoryStart.heapUsed,
heapUsedEndBytes: memoryEnd.heapUsed,
},
controls: {
publicVisibilityEnabled: false,
schedulesEnabled: false,
maxRealScanAdmissions: policy.maxRealScanAdmissions,
batchSize,
},
});
} catch (error) {
return jsonResponse(
{
error: "skills_sh_catalog_test_failed",
message: error instanceof Error ? error.message : "Unknown Test gate failure",
},
502,
);
}
});
+538
View File
@@ -0,0 +1,538 @@
/* @vitest-environment node */
import { describe, expect, it, vi } from "vitest";
import {
captureSkillsShCatalogTestSnapshot,
fetchSkillsShCatalogDetail,
fetchSkillsShCatalogPage,
fetchSkillsShCatalogTestPage,
getSkillsShCatalogTestSourcePolicy,
SkillsShCatalogOwnerProofRequiredError,
validateSkillsShCatalogGitHubOwnerProof,
} from "./skillsShCatalogSource";
describe("skills.sh Vercel source boundary", () => {
it("uses only the injected Vercel OIDC token for source authentication", async () => {
const fetchImpl = vi.fn(async () => {
return new Response(
JSON.stringify({
data: [],
pagination: { page: 0, perPage: 500, total: 0, hasMore: false },
}),
);
});
await fetchSkillsShCatalogPage(
{ page: 0, perPage: 500 },
{
env: { VERCEL_OIDC_TOKEN: "short-lived-vercel-oidc" },
fetchImpl,
},
);
expect(fetchImpl).toHaveBeenCalledWith("https://skills.sh/api/v1/skills?page=0&per_page=500", {
headers: {
Accept: "application/json",
Authorization: "Bearer short-lived-vercel-oidc",
},
});
});
it("accepts a request-bound OIDC token without requiring an environment copy", async () => {
const fetchImpl = vi.fn(async () => {
return new Response(
JSON.stringify({
data: [],
pagination: { page: 0, perPage: 500, total: 0, hasMore: false },
}),
);
});
await fetchSkillsShCatalogPage(
{ page: 0, perPage: 500 },
{
env: {},
oidcToken: "request-bound-oidc",
fetchImpl,
},
);
expect(fetchImpl).toHaveBeenCalledWith(expect.any(String), {
headers: expect.objectContaining({
Authorization: "Bearer request-bound-oidc",
}),
});
});
it("fails closed without OIDC and above the 500-row boundary", async () => {
await expect(
fetchSkillsShCatalogPage({ page: 0, perPage: 500 }, { env: {}, fetchImpl: vi.fn() }),
).rejects.toThrow("requires VERCEL_OIDC_TOKEN");
await expect(
fetchSkillsShCatalogPage(
{ page: 0, perPage: 501 },
{ env: { VERCEL_OIDC_TOKEN: "token" }, fetchImpl: vi.fn() },
),
).rejects.toThrow("perPage must be an integer between 1 and 500");
});
it("preserves repository-qualified detail ids", async () => {
const fetchImpl = vi.fn(async () => {
return new Response(
JSON.stringify({
id: "anthropics/claude-code/frontend-design",
source: "anthropics/claude-code",
slug: "frontend-design",
installs: 1,
hash: "hash",
files: [],
}),
);
});
await fetchSkillsShCatalogDetail("anthropics/claude-code/frontend-design", {
env: { VERCEL_OIDC_TOKEN: "token" },
fetchImpl,
});
expect(fetchImpl).toHaveBeenCalledWith(
"https://skills.sh/api/v1/skills/anthropics/claude-code/frontend-design",
expect.any(Object),
);
});
it("requires the Test build, Preview runtime, baked backend, and explicit enable", () => {
expect(
getSkillsShCatalogTestSourcePolicy({
VERCEL_ENV: "preview",
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED: "1",
}),
).toMatchObject({ allowed: false });
expect(
getSkillsShCatalogTestSourcePolicy({
VERCEL_ENV: "preview",
VERCEL_TARGET_ENV: "test",
VITE_CLAWHUB_DEPLOY_ENV: "test",
VITE_CONVEX_URL: "https://academic-chihuahua-392.convex.cloud",
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED: "1",
}),
).toEqual({
allowed: true,
environment: "test",
maxDiscoveryRows: 500,
maxRealScanAdmissions: 10,
});
});
it("requires exact authenticated immutable owner coverage for the selected live set", () => {
expect(
Array.from(
validateSkillsShCatalogGitHubOwnerProof(["anthropics", "nvidia"], {
authentication: "clawhub-github-authenticated",
provenance: "live-github",
fetches: 2,
reused: 0,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
}),
),
).toEqual([
["anthropics", 76_263_028],
["nvidia", 1_728_152],
]);
expect(() =>
validateSkillsShCatalogGitHubOwnerProof(["anthropics", "nvidia"], {
authentication: "clawhub-github-authenticated",
provenance: "live-github",
fetches: 1,
reused: 0,
owners: [{ owner: "nvidia", login: "nvidia", id: 1_728_152 }],
}),
).toThrow("lacks complete authenticated GitHub owner proof");
expect(() =>
validateSkillsShCatalogGitHubOwnerProof(["nvidia"], {
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live",
fetches: 0,
reused: 1,
owners: [{ owner: "nvidia", login: "renamed-owner", id: 1_728_152 }],
}),
).toThrow("invalid authenticated GitHub owner proof");
});
it("selects 500 hash-qualified rows before requiring exact live owner proofs", async () => {
const listRows = [
{
id: "anthropics/skills/frontend-design",
installUrl: "https://github.com/anthropics/skills",
installs: 100,
name: "Frontend Design",
slug: "frontend-design",
source: "anthropics/skills",
sourceType: "github",
url: "https://skills.sh/anthropics/skills/frontend-design",
},
{
id: "anthropics/claude-code/frontend-design",
installUrl: "https://github.com/anthropics/claude-code",
installs: 99,
name: "Frontend Design",
slug: "frontend-design",
source: "anthropics/claude-code",
sourceType: "github",
url: "https://skills.sh/anthropics/claude-code/frontend-design",
},
...Array.from({ length: 498 }, (_, index) => ({
id: `owner/repo-${index}/skill-${index}`,
installUrl: `https://github.com/owner/repo-${index}`,
installs: index,
name: `Skill ${index}`,
slug: `skill-${index}`,
source: `owner/repo-${index}`,
sourceType: "github",
url: `https://skills.sh/owner/repo-${index}/skill-${index}`,
})),
];
const nvidiaRows = Array.from({ length: 10 }, (_, index) => ({
id: `nvidia/skills/nvidia-skill-${index}`,
installUrl: "https://github.com/nvidia/skills",
installs: 1_000 - index,
name: `NVIDIA Skill ${index}`,
slug: `nvidia-skill-${index}`,
source: "nvidia/skills",
sourceType: "github",
url: `https://skills.sh/nvidia/skills/nvidia-skill-${index}`,
}));
const detailUrls: string[] = [];
const fetchImpl = vi.fn(async (urlInput: string | URL | Request) => {
const url = String(urlInput);
if (url.includes("/skills?")) {
return new Response(
JSON.stringify({
data: listRows,
pagination: {
page: 0,
perPage: 500,
total: 500,
hasMore: false,
},
}),
);
}
if (url.includes("/skills/search?")) {
return new Response(JSON.stringify({ data: nvidiaRows }));
}
detailUrls.push(url);
const id = decodeURIComponent(url.split("/api/v1/skills/")[1] ?? "");
if (id === "owner/repo-0/skill-0") {
return new Response(
JSON.stringify({
id,
source: "owner/repo-0",
slug: "skill-0",
installs: 1,
hash: null,
files: null,
}),
);
}
if (id === "owner/repo-1/skill-1") {
return new Response(
JSON.stringify({
id,
source: "owner/repo-1",
slug: "skill-1",
installs: 1,
hash: "b".repeat(64),
files: null,
}),
);
}
if (id === "owner/repo-2/skill-2") {
return new Response(
JSON.stringify({
id,
source: "owner/repo-2",
slug: "skill-2",
installs: 1,
hash: "c".repeat(64),
files: [{ name: "SKILL.md" }],
}),
);
}
return new Response(
JSON.stringify({
id,
source: id.split("/").slice(0, 2).join("/"),
slug: id.split("/").at(-1),
installs: 1,
hash: "a".repeat(64),
files: [{ name: "SKILL.md", content: `# ${id}` }],
}),
);
});
const readOidc = async () => "oidc";
const verifyOidc = async () => ({
payload: {
owner_id: "team_pLdjXbfy0XvPRiNmAygTjTSH",
project_id: "prj_UVAJPNPYrBwTEkPJwkpEySsge8Mc",
environment: "test",
sub: "owner:project:test",
aud: "https://vercel.com",
iss: "https://oidc.vercel.com",
},
});
const options = {
env: {
VERCEL_ENV: "preview",
VITE_CLAWHUB_DEPLOY_ENV: "test",
VITE_CONVEX_URL: "https://academic-chihuahua-392.convex.cloud",
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED: "1",
},
fetchImpl: fetchImpl as typeof fetch,
getOidcToken: readOidc,
verifyOidcToken: verifyOidc,
readConvexControl: async () => ({
mode: "staging-live" as const,
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
publicVisibilityEnabled: false,
}),
admitExternalIds: ["nvidia/skills/nvidia-skill-0"],
};
await expect(
captureSkillsShCatalogTestSnapshot({
...options,
admitExternalIds: ["owner/repo-1/skill-1"],
}),
).rejects.toThrow("live admission lacks artifact files");
detailUrls.length = 0;
await expect(
captureSkillsShCatalogTestSnapshot({
...options,
admitExternalIds: ["owner/repo-2/skill-2"],
}),
).rejects.toThrow("live admission has incomplete artifact files");
detailUrls.length = 0;
const preflight = await captureSkillsShCatalogTestSnapshot(options).catch((error) => error);
expect(preflight).toBeInstanceOf(SkillsShCatalogOwnerProofRequiredError);
expect(preflight).toMatchObject({
owners: ["anthropics", "nvidia", "owner"],
sourcePreflight: {
skillsShFetches: 506,
listFetches: 1,
searchFetches: 1,
detailFetches: 504,
selection: {
rows: 500,
nvidiaRows: 10,
skippedIncompleteDetails: 1,
},
},
});
expect(detailUrls).toHaveLength(504);
detailUrls.length = 0;
const snapshot = await captureSkillsShCatalogTestSnapshot({
...options,
githubOwnerProof: {
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live",
fetches: 0,
reused: 3,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
{ owner: "owner", login: "owner", id: 123 },
],
},
});
expect(snapshot.rows).toHaveLength(500);
expect(snapshot.selection).toMatchObject({
rows: 500,
nvidiaRows: 10,
requiredCollisionIds: [
"anthropics/skills/frontend-design",
"anthropics/claude-code/frontend-design",
],
skippedIncompleteDetails: 1,
});
expect(snapshot.rows.some((row) => row.externalId === "owner/repo-0/skill-0")).toBe(false);
expect(snapshot.artifacts).toHaveLength(1);
expect(snapshot.metrics).toMatchObject({
skillsShFetches: 506,
listFetches: 1,
searchFetches: 1,
detailFetches: 504,
githubOwnerFetches: 0,
githubOwnerIdsReused: 3,
githubOwnerProofProvenance: "stored-authenticated-staging-live",
skippedIncompleteDetails: 1,
});
expect(detailUrls).toHaveLength(504);
});
it("rejects an ordinary Preview even when spoofable Test strings are present", async () => {
await expect(
fetchSkillsShCatalogTestPage({
env: {
VERCEL_ENV: "preview",
VERCEL_TARGET_ENV: "test",
VITE_CLAWHUB_DEPLOY_ENV: "test",
VITE_CONVEX_URL: "https://academic-chihuahua-392.convex.cloud",
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED: "1",
},
getOidcToken: async () => "ordinary-preview-token",
verifyOidcToken: async () => {
throw new Error("unexpected Vercel project");
},
readConvexControl: async () => ({
mode: "staging-live",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
publicVisibilityEnabled: false,
}),
}),
).rejects.toThrow("unexpected Vercel project");
});
it("rejects a verified ClawHub Preview token without the custom Test environment claim", async () => {
await expect(
fetchSkillsShCatalogTestPage({
env: {
VERCEL_ENV: "preview",
VERCEL_TARGET_ENV: "test",
VITE_CLAWHUB_DEPLOY_ENV: "test",
VITE_CONVEX_URL: "https://academic-chihuahua-392.convex.cloud",
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED: "1",
},
getOidcToken: async () => "ordinary-preview-token",
verifyOidcToken: async () => ({
payload: {
owner_id: "team_pLdjXbfy0XvPRiNmAygTjTSH",
project_id: "prj_UVAJPNPYrBwTEkPJwkpEySsge8Mc",
environment: "preview",
sub: "owner:project:preview",
aud: "https://vercel.com",
iss: "https://oidc.vercel.com",
},
}),
readConvexControl: async () => ({
mode: "staging-live",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
publicVisibilityEnabled: false,
}),
}),
).rejects.toThrow("verified ClawHub Vercel identity");
});
it("fetches through the verified request token only when the dark Convex control allows it", async () => {
const rows = Array.from({ length: 500 }, (_, index) => ({
id: `owner/repo/skill-${index}`,
installUrl: null,
installs: index,
name: `Skill ${index}`,
slug: `skill-${index}`,
source: "owner/repo",
sourceType: "github",
url: `https://skills.sh/owner/repo/skill-${index}`,
}));
const fetchImpl = vi.fn(async () => {
return new Response(
JSON.stringify({
data: rows,
pagination: {
page: 0,
perPage: 500,
total: 500,
hasMore: false,
},
}),
);
});
const env = {
VERCEL_ENV: "preview",
VITE_CLAWHUB_DEPLOY_ENV: "test",
VITE_CONVEX_URL: "https://academic-chihuahua-392.convex.cloud",
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED: "1",
};
const getOidcToken = vi.fn(async () => "request-token");
const verifyOidcToken = vi.fn(async () => ({
payload: {
owner_id: "team_pLdjXbfy0XvPRiNmAygTjTSH",
project_id: "prj_UVAJPNPYrBwTEkPJwkpEySsge8Mc",
environment: "test",
sub: "owner:project:test",
aud: "https://vercel.com",
iss: "https://oidc.vercel.com",
},
}));
const result = await fetchSkillsShCatalogTestPage({
env,
fetchImpl,
getOidcToken,
verifyOidcToken,
readConvexControl: async () => ({
mode: "staging-live",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
publicVisibilityEnabled: false,
}),
});
expect(result.page.data).toHaveLength(500);
expect(getOidcToken).toHaveBeenCalledOnce();
expect(verifyOidcToken).toHaveBeenCalledWith("request-token", {
projectId: "prj_UVAJPNPYrBwTEkPJwkpEySsge8Mc",
ownerId: "team_pLdjXbfy0XvPRiNmAygTjTSH",
environment: "test",
});
expect(result.controls).toEqual({
maxDiscoveryRows: 500,
maxRealScanAdmissions: 10,
publicVisibilityEnabled: false,
});
expect(fetchImpl).toHaveBeenCalledWith(
"https://skills.sh/api/v1/skills?page=0&per_page=500",
expect.objectContaining({
headers: expect.objectContaining({
Authorization: "Bearer request-token",
}),
}),
);
await expect(
fetchSkillsShCatalogTestPage({
env,
fetchImpl,
getOidcToken: async () => "request-token",
verifyOidcToken,
readConvexControl: async () => ({
mode: "fixture",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
publicVisibilityEnabled: false,
}),
}),
).rejects.toThrow("dark Convex staging control");
});
});
+678
View File
@@ -0,0 +1,678 @@
import { createHash } from "node:crypto";
import { getVercelOidcToken, verifyVercelOidcToken, type VercelOidcPayload } from "@vercel/oidc";
const SKILLS_SH_API_BASE = "https://skills.sh/api/v1";
const MAX_SOURCE_PAGE_SIZE = 500;
const MAX_TEST_SCAN_ADMISSIONS = 10;
const DETAIL_CONCURRENCY = 8;
const CLAWHUB_VERCEL_OWNER_ID = "team_pLdjXbfy0XvPRiNmAygTjTSH";
const CLAWHUB_VERCEL_PROJECT_ID = "prj_UVAJPNPYrBwTEkPJwkpEySsge8Mc";
const CLAWHUB_TEST_CONVEX_URL = "https://academic-chihuahua-392.convex.cloud";
export type SkillsShCatalogSourceEnv = {
CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED?: string;
VERCEL_ENV?: string;
VERCEL_OIDC_TOKEN?: string;
VERCEL_TARGET_ENV?: string;
VITE_CLAWHUB_DEPLOY_ENV?: string;
VITE_CONVEX_URL?: string;
};
export type SkillsShCatalogListRow = {
id: string;
installUrl: string | null;
installs: number;
name: string;
slug: string;
source: string;
sourceType: string;
url: string;
};
export type SkillsShCatalogDetail = {
id: string;
source: string;
slug: string;
installs: number;
hash: string | null;
files: Array<{
name?: unknown;
content?: unknown;
}> | null;
};
type SkillsShCatalogPage = {
data: SkillsShCatalogListRow[];
pagination: {
page: number;
perPage: number;
total: number;
hasMore: boolean;
};
};
type SkillsShCatalogSearch = {
data: SkillsShCatalogListRow[];
};
type HashQualifiedSkillsShCatalogDetail = SkillsShCatalogDetail & {
hash: string;
};
function assertIntegerInRange(name: string, value: number, min: number, max: number) {
if (!Number.isInteger(value) || value < min || value > max) {
throw new Error(`${name} must be an integer between ${min} and ${max}`);
}
}
function requireOidcToken(env: SkillsShCatalogSourceEnv, requestOidcToken?: string) {
const token = requestOidcToken?.trim() || env.VERCEL_OIDC_TOKEN?.trim();
if (!token) {
throw new Error("skills.sh catalog source requires VERCEL_OIDC_TOKEN");
}
return token;
}
async function fetchSkillsShJson<T>(
path: string,
options: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
oidcToken?: string;
} = {},
): Promise<T> {
const env = options.env ?? process.env;
const fetchImpl = options.fetchImpl ?? fetch;
const response = await fetchImpl(`${SKILLS_SH_API_BASE}${path}`, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${requireOidcToken(env, options.oidcToken)}`,
},
});
if (!response.ok) {
throw new Error(`skills.sh catalog source returned HTTP ${response.status}`);
}
return (await response.json()) as T;
}
export async function fetchSkillsShCatalogPage(
args: {
page: number;
perPage: number;
},
options: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
oidcToken?: string;
} = {},
) {
assertIntegerInRange("page", args.page, 0, 100_000);
assertIntegerInRange("perPage", args.perPage, 1, MAX_SOURCE_PAGE_SIZE);
return await fetchSkillsShJson<SkillsShCatalogPage>(
`/skills?page=${args.page}&per_page=${args.perPage}`,
options,
);
}
export async function searchSkillsShCatalog(
args: {
query: string;
owner?: string;
limit: number;
},
options: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
oidcToken?: string;
} = {},
) {
assertIntegerInRange("limit", args.limit, 1, MAX_SOURCE_PAGE_SIZE);
const params = new URLSearchParams({
q: args.query,
limit: String(args.limit),
});
if (args.owner) params.set("owner", args.owner);
return await fetchSkillsShJson<SkillsShCatalogSearch>(
`/skills/search?${params.toString()}`,
options,
);
}
export async function fetchSkillsShCatalogDetail(
id: string,
options: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
oidcToken?: string;
} = {},
) {
const normalizedId = id
.split("/")
.map((part) => encodeURIComponent(part))
.join("/");
if (normalizedId.split("/").length !== 3) {
throw new Error("skills.sh catalog detail id must be owner/repo/skill");
}
return await fetchSkillsShJson<SkillsShCatalogDetail>(`/skills/${normalizedId}`, options);
}
export function getSkillsShCatalogTestSourcePolicy(env: SkillsShCatalogSourceEnv = process.env) {
if (env.VITE_CLAWHUB_DEPLOY_ENV !== "test") {
return {
allowed: false as const,
environment: env.VITE_CLAWHUB_DEPLOY_ENV?.trim() || "unknown",
reason: "skills.sh live Test discovery requires the Test build marker",
};
}
if (env.VERCEL_ENV !== "preview") {
return {
allowed: false as const,
environment: env.VERCEL_ENV?.trim() || "unknown",
reason: "skills.sh live Test discovery requires the Vercel Preview runtime",
};
}
if (env.VITE_CONVEX_URL !== CLAWHUB_TEST_CONVEX_URL) {
return {
allowed: false as const,
environment: "test",
reason: "skills.sh live Test discovery requires the baked Test Convex backend",
};
}
if (env.CLAWHUB_SKILLS_SH_TEST_LIVE_FETCH_ENABLED !== "1") {
return {
allowed: false as const,
environment: "test",
reason: "skills.sh live Test discovery is disabled",
};
}
return {
allowed: true as const,
environment: "test",
maxDiscoveryRows: MAX_SOURCE_PAGE_SIZE,
maxRealScanAdmissions: MAX_TEST_SCAN_ADMISSIONS,
};
}
type VerifyVercelOidc = (
token: string,
options: {
projectId: string;
ownerId: string;
environment: string;
},
) => Promise<{ payload: VercelOidcPayload }>;
type SkillsShCatalogTestControl = {
mode: "off" | "fixture" | "staging-live";
discoveryEnabled: boolean;
writesEnabled: boolean;
scanPlanningEnabled: boolean;
maxEntriesPerRun: number;
publicVisibilityEnabled: boolean;
};
export type SkillsShCatalogGitHubOwnerProof = {
authentication: "clawhub-github-authenticated";
provenance:
| "live-github"
| "stored-authenticated-staging-live"
| "stored-authenticated-staging-live+live-github";
fetches: number;
reused: number;
owners: Array<{ owner: string; id: number; login: string }>;
};
export class SkillsShCatalogOwnerProofRequiredError extends Error {
constructor(
readonly owners: string[],
readonly sourcePreflight: {
skillsShFetches: number;
listFetches: number;
searchFetches: number;
detailFetches: number;
selection: {
rows: number;
nvidiaRows: number;
requiredCollisionIds: readonly string[];
skippedIncompleteDetails: number;
};
},
) {
super("skills.sh live Test source requires authenticated GitHub owner proofs");
this.name = "SkillsShCatalogOwnerProofRequiredError";
}
}
export function validateSkillsShCatalogGitHubOwnerProof(
selectedOwners: readonly string[],
proof: SkillsShCatalogGitHubOwnerProof,
) {
const expectedProvenance =
proof.reused === 0
? "live-github"
: proof.fetches === 0
? "stored-authenticated-staging-live"
: "stored-authenticated-staging-live+live-github";
if (
proof.authentication !== "clawhub-github-authenticated" ||
!Number.isInteger(proof.fetches) ||
proof.fetches < 0 ||
!Number.isInteger(proof.reused) ||
proof.reused < 0 ||
proof.fetches + proof.reused !== selectedOwners.length ||
proof.provenance !== expectedProvenance ||
proof.owners.length !== selectedOwners.length
) {
throw new Error("skills.sh live Test source lacks complete authenticated GitHub owner proof");
}
const selected = new Set(selectedOwners);
const ids = new Map<string, number>();
for (const resolvedOwner of proof.owners) {
const owner = resolvedOwner.owner.trim().toLowerCase();
const login = resolvedOwner.login.trim().toLowerCase();
if (
!selected.has(owner) ||
login !== owner ||
ids.has(owner) ||
!Number.isSafeInteger(resolvedOwner.id) ||
resolvedOwner.id <= 0
) {
throw new Error(
"skills.sh live Test source returned invalid authenticated GitHub owner proof",
);
}
ids.set(owner, resolvedOwner.id);
}
for (const owner of selectedOwners) {
if (!ids.has(owner)) {
throw new Error(
`skills.sh live Test source lacks an authenticated immutable owner id for ${owner}`,
);
}
}
return ids;
}
async function authorizeSkillsShCatalogTestRequest(
options: {
env?: SkillsShCatalogSourceEnv;
getOidcToken?: () => Promise<string>;
verifyOidcToken?: VerifyVercelOidc;
} = {},
) {
const env = options.env ?? process.env;
const policy = getSkillsShCatalogTestSourcePolicy(env);
if (!policy.allowed) throw new Error(policy.reason);
const getOidcToken = options.getOidcToken ?? getVercelOidcToken;
const verifyOidcToken = options.verifyOidcToken ?? verifyVercelOidcToken;
const token = await getOidcToken();
const verified = await verifyOidcToken(token, {
projectId: CLAWHUB_VERCEL_PROJECT_ID,
ownerId: CLAWHUB_VERCEL_OWNER_ID,
environment: "test",
});
if (
verified.payload.project_id !== CLAWHUB_VERCEL_PROJECT_ID ||
verified.payload.owner_id !== CLAWHUB_VERCEL_OWNER_ID ||
verified.payload.environment !== "test"
) {
throw new Error("skills.sh live Test discovery requires verified ClawHub Vercel identity");
}
return {
...policy,
oidcToken: token,
verifiedIdentity: {
ownerId: verified.payload.owner_id,
projectId: verified.payload.project_id,
environment: verified.payload.environment,
},
};
}
export async function fetchSkillsShCatalogTestPage(options: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
getOidcToken?: () => Promise<string>;
verifyOidcToken?: VerifyVercelOidc;
readConvexControl: () => Promise<SkillsShCatalogTestControl>;
}) {
const authorization = await authorizeSkillsShCatalogTestRequest(options);
const control = await options.readConvexControl();
if (
control.mode !== "staging-live" ||
!control.discoveryEnabled ||
!control.writesEnabled ||
!control.scanPlanningEnabled ||
control.maxEntriesPerRun < 1 ||
control.maxEntriesPerRun > authorization.maxDiscoveryRows ||
control.publicVisibilityEnabled
) {
throw new Error("skills.sh live Test discovery requires the dark Convex staging control");
}
const page = await fetchSkillsShCatalogPage(
{ page: 0, perPage: authorization.maxDiscoveryRows },
{
env: options.env,
fetchImpl: options.fetchImpl,
oidcToken: authorization.oidcToken,
},
);
if (page.data.length > control.maxEntriesPerRun) {
throw new Error("skills.sh live Test discovery exceeded the Convex run budget");
}
return {
page,
verifiedIdentity: authorization.verifiedIdentity,
controls: {
maxDiscoveryRows: control.maxEntriesPerRun,
maxRealScanAdmissions: authorization.maxRealScanAdmissions,
publicVisibilityEnabled: false,
},
};
}
const REQUIRED_COLLISION_IDS = [
"anthropics/skills/frontend-design",
"anthropics/claude-code/frontend-design",
] as const;
function isGitHubCatalogRow(row: SkillsShCatalogListRow) {
const source = row.source.trim().toLowerCase();
const slug = row.slug.trim().toLowerCase();
return (
row.sourceType === "github" &&
source.split("/").length === 2 &&
/^[a-z0-9][a-z0-9-]*$/.test(slug) &&
row.id.trim().toLowerCase() === `${source}/${slug}`
);
}
function normalizeListRow(row: SkillsShCatalogListRow) {
const [owner = "", repo = ""] = row.source.split("/");
const slug = row.slug.trim().toLowerCase();
return {
...row,
owner: owner.trim().toLowerCase(),
repo: repo.trim().toLowerCase(),
slug,
externalId: `${owner.trim().toLowerCase()}/${repo.trim().toLowerCase()}/${slug}`,
};
}
function sha256Hex(bytes: Uint8Array | string) {
return createHash("sha256").update(bytes).digest("hex");
}
type CompleteArtifactFile = { name: string; content: string };
function hasCompleteArtifactFiles(
files: SkillsShCatalogDetail["files"],
): files is CompleteArtifactFile[] {
return (
Array.isArray(files) &&
files.length > 0 &&
files.every(
(file) =>
typeof file.name === "string" &&
file.name.trim().length > 0 &&
typeof file.content === "string",
)
);
}
function buildArtifact(detail: SkillsShCatalogDetail) {
if (!hasCompleteArtifactFiles(detail.files)) {
throw new Error(`skills.sh live admission has incomplete artifact files: ${detail.id}`);
}
const files = detail.files
.map((file) => {
const bytes = Buffer.from(file.content, "utf8");
return {
path: file.name,
size: bytes.byteLength,
sha256: sha256Hex(bytes),
contentType: file.name.toLowerCase().endsWith(".md")
? "text/markdown; charset=utf-8"
: "text/plain; charset=utf-8",
contentBase64: bytes.toString("base64"),
};
})
.sort((left, right) => left.path.localeCompare(right.path));
const manifest = files.map((file) => `${file.path}\0${file.sha256}\n`).join("");
return {
artifactContentHash: sha256Hex(manifest),
files,
};
}
function createSkillsShFetchOptions(
env: SkillsShCatalogSourceEnv | undefined,
fetchImpl: typeof fetch | undefined,
value: string,
) {
return {
env,
fetchImpl,
oidcToken: value,
};
}
async function selectSkillsShCatalogTestRows(
fetchOptions: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
oidcToken?: string;
},
requiredArtifactIds: ReadonlySet<string>,
) {
const candidates = new Map<string, ReturnType<typeof normalizeListRow>>();
let listFetches = 0;
for (let page = 0; candidates.size < MAX_SOURCE_PAGE_SIZE + 100; page += 1) {
const response = await fetchSkillsShCatalogPage(
{ page, perPage: MAX_SOURCE_PAGE_SIZE },
fetchOptions,
);
listFetches += 1;
for (const row of response.data) {
if (!isGitHubCatalogRow(row)) continue;
const normalized = normalizeListRow(row);
candidates.set(normalized.externalId, normalized);
}
if (!response.pagination.hasMore) break;
}
const nvidia = await searchSkillsShCatalog(
{ query: "nvidia", owner: "nvidia", limit: 200 },
fetchOptions,
);
const nvidiaRows = nvidia.data
.filter(isGitHubCatalogRow)
.map(normalizeListRow)
.filter((row) => row.owner === "nvidia")
.slice(0, 10);
const requiredIds = new Set([
...REQUIRED_COLLISION_IDS,
...nvidiaRows.map((row) => row.externalId),
...requiredArtifactIds,
]);
for (const row of nvidiaRows) candidates.set(row.externalId, row);
for (const id of requiredIds) {
if (!candidates.has(id)) throw new Error(`Required skills.sh live row is missing: ${id}`);
}
const candidateRows = [
...Array.from(requiredIds, (id) => candidates.get(id)!),
...Array.from(candidates.values()).filter((row) => !requiredIds.has(row.externalId)),
];
const selected: Array<{
row: ReturnType<typeof normalizeListRow>;
detail: HashQualifiedSkillsShCatalogDetail;
}> = [];
let detailFetches = 0;
let skippedIncompleteDetails = 0;
for (
let offset = 0;
offset < candidateRows.length && selected.length < MAX_SOURCE_PAGE_SIZE;
offset += DETAIL_CONCURRENCY
) {
const batch = candidateRows.slice(offset, offset + DETAIL_CONCURRENCY);
const details = await Promise.all(
batch.map(async (row) => await fetchSkillsShCatalogDetail(row.externalId, fetchOptions)),
);
detailFetches += details.length;
for (let index = 0; index < batch.length; index += 1) {
const row = batch[index]!;
const detail = details[index]!;
const hasExactHash =
detail.id.trim().toLowerCase() === row.externalId &&
typeof detail.hash === "string" &&
/^[a-f0-9]{64}$/i.test(detail.hash);
if (!hasExactHash) {
if (requiredIds.has(row.externalId) || requiredArtifactIds.has(row.externalId)) {
throw new Error(
`Required skills.sh live row lacks an exact detail hash: ${row.externalId}`,
);
}
skippedIncompleteDetails += 1;
continue;
}
if (
requiredArtifactIds.has(row.externalId) &&
(!Array.isArray(detail.files) || detail.files.length < 1)
) {
throw new Error(`skills.sh live admission lacks artifact files: ${row.externalId}`);
}
if (requiredArtifactIds.has(row.externalId) && !hasCompleteArtifactFiles(detail.files)) {
throw new Error(
`skills.sh live admission has incomplete artifact files: ${row.externalId}`,
);
}
selected.push({ row, detail: detail as HashQualifiedSkillsShCatalogDetail });
if (selected.length === MAX_SOURCE_PAGE_SIZE) break;
}
}
if (selected.length !== MAX_SOURCE_PAGE_SIZE) {
throw new Error(`Expected ${MAX_SOURCE_PAGE_SIZE} hash-qualified live rows`);
}
return {
selected,
selectedOwners: Array.from(new Set(selected.map(({ row }) => row.owner))).sort(),
listFetches,
detailFetches,
skippedIncompleteDetails,
selection: {
rows: selected.length,
nvidiaRows: selected.filter(({ row }) => row.owner === "nvidia").length,
requiredCollisionIds: REQUIRED_COLLISION_IDS,
skippedIncompleteDetails,
},
};
}
export async function captureSkillsShCatalogTestSnapshot(options: {
env?: SkillsShCatalogSourceEnv;
fetchImpl?: typeof fetch;
getOidcToken?: () => Promise<string>;
verifyOidcToken?: VerifyVercelOidc;
readConvexControl: () => Promise<SkillsShCatalogTestControl>;
admitExternalIds?: string[];
githubOwnerProof?: SkillsShCatalogGitHubOwnerProof;
resolveGitHubOwners?: (owners: string[]) => Promise<SkillsShCatalogGitHubOwnerProof>;
}) {
const startedAt = Date.now();
const authorization = await authorizeSkillsShCatalogTestRequest(options);
const control = await options.readConvexControl();
if (
control.mode !== "staging-live" ||
!control.discoveryEnabled ||
!control.writesEnabled ||
!control.scanPlanningEnabled ||
control.maxEntriesPerRun !== MAX_SOURCE_PAGE_SIZE ||
control.publicVisibilityEnabled
) {
throw new Error("skills.sh live Test capture requires the exact dark 500-row control");
}
const fetchOptions = createSkillsShFetchOptions(
options.env,
options.fetchImpl,
authorization.oidcToken,
);
const admitted = new Set(
(options.admitExternalIds ?? []).map((externalId) => externalId.trim().toLowerCase()),
);
if (admitted.size > authorization.maxRealScanAdmissions) {
throw new Error(
`skills.sh live Test admission cannot exceed ${authorization.maxRealScanAdmissions}`,
);
}
const selection = await selectSkillsShCatalogTestRows(fetchOptions, admitted);
const sourcePreflight = {
skillsShFetches: selection.listFetches + 1 + selection.detailFetches,
listFetches: selection.listFetches,
searchFetches: 1,
detailFetches: selection.detailFetches,
selection: selection.selection,
};
const githubOwnerProof =
options.githubOwnerProof ??
(options.resolveGitHubOwners
? await options.resolveGitHubOwners(selection.selectedOwners)
: null);
if (!githubOwnerProof) {
throw new SkillsShCatalogOwnerProofRequiredError(selection.selectedOwners, sourcePreflight);
}
const githubOwnerIds = validateSkillsShCatalogGitHubOwnerProof(
selection.selectedOwners,
githubOwnerProof,
);
const selected = selection.selected;
const rows = selected.map(({ row, detail }) => {
const githubOwnerId = githubOwnerIds.get(row.owner)!;
return {
externalId: row.externalId,
githubOwnerId,
owner: row.owner,
repo: row.repo,
slug: row.slug,
displayName: row.name.trim() || row.slug,
sourceUrl: row.url,
githubRepoUrl: row.installUrl ?? `https://github.com/${row.owner}/${row.repo}`,
sourceContentHash: detail.hash.toLowerCase(),
installs: row.installs,
};
});
const artifacts = selected
.filter(({ row }) => admitted.has(row.externalId))
.map(({ row, detail }) => ({
externalId: row.externalId,
...buildArtifact(detail),
}));
if (artifacts.length !== admitted.size) {
throw new Error("skills.sh live Test admission artifact is not present in the selected 500");
}
return {
snapshotId: `skills-sh-test-live-500:${sha256Hex(
rows.map((row) => `${row.externalId}:${row.sourceContentHash}\n`).join(""),
).slice(0, 16)}`,
capturedAt: new Date().toISOString(),
rows,
artifacts,
verifiedIdentity: authorization.verifiedIdentity,
selection: {
rows: rows.length,
nvidiaRows: rows.filter((row) => row.owner === "nvidia").length,
requiredCollisionIds: REQUIRED_COLLISION_IDS,
skippedIncompleteDetails: selection.skippedIncompleteDetails,
},
metrics: {
runtimeMs: Date.now() - startedAt,
skillsShFetches: selection.listFetches + 1 + selection.detailFetches,
listFetches: selection.listFetches,
searchFetches: 1,
detailFetches: selection.detailFetches,
githubOwnerFetches: githubOwnerProof.fetches,
githubOwnerIdsReused: githubOwnerProof.reused,
githubOwnerProofProvenance: githubOwnerProof.provenance,
skippedIncompleteDetails: selection.skippedIncompleteDetails,
},
};
}
+379
View File
@@ -0,0 +1,379 @@
/* @vitest-environment node */
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const getHeaderMock = vi.fn();
const getVercelOidcTokenMock = vi.fn();
const readBodyMock = vi.fn();
const captureSnapshotMock = vi.fn();
const sourcePolicyMock = vi.fn();
vi.mock("h3", () => ({
defineEventHandler: (handler: unknown) => handler,
getHeader: (...args: unknown[]) => getHeaderMock(...args),
readBody: (...args: unknown[]) => readBodyMock(...args),
}));
vi.mock("@vercel/oidc", () => ({
getVercelOidcToken: (...args: unknown[]) => getVercelOidcTokenMock(...args),
}));
vi.mock("./skillsShCatalogSource", () => ({
captureSkillsShCatalogTestSnapshot: (...args: unknown[]) => captureSnapshotMock(...args),
getSkillsShCatalogTestSourcePolicy: (...args: unknown[]) => sourcePolicyMock(...args),
}));
describe("skills.sh permanent Test operator route", () => {
beforeEach(() => {
getHeaderMock.mockReset();
getVercelOidcTokenMock.mockReset();
readBodyMock.mockReset();
captureSnapshotMock.mockReset();
sourcePolicyMock.mockReset();
sourcePolicyMock.mockReturnValue({
allowed: true,
environment: "test",
maxDiscoveryRows: 500,
maxRealScanAdmissions: 10,
});
getHeaderMock.mockImplementation((_event: unknown, name: string) => {
if (name === "authorization") return "Bearer operator-token";
return undefined;
});
getVercelOidcTokenMock.mockResolvedValue("request-oidc-token");
readBodyMock.mockResolvedValue({
allowlist: ["nvidia/skills/aiq-deploy"],
reason: "bounded Test proof",
});
captureSnapshotMock.mockImplementation(async (options) => {
expect(await options.getOidcToken()).toBe("request-oidc-token");
expect(options.admitExternalIds).toEqual(["nvidia/skills/aiq-deploy"]);
expect(await options.resolveGitHubOwners(["anthropics", "nvidia"])).toEqual({
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live",
fetches: 0,
reused: 2,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
});
return {
snapshotId: "skills-sh-test-live-500:abc",
capturedAt: "2026-07-21T00:00:00.000Z",
rows: Array.from({ length: 500 }, (_, index) => ({
externalId: `owner/repo/skill-${index}`,
})),
artifacts: [
{
externalId: "nvidia/skills/aiq-deploy",
artifactContentHash: "a".repeat(64),
files: [],
},
],
verifiedIdentity: {
ownerId: "team",
projectId: "project",
environment: "test",
},
selection: {
rows: 500,
nvidiaRows: 10,
requiredCollisionIds: [],
},
metrics: {
runtimeMs: 100,
skillsShFetches: 502,
listFetches: 1,
searchFetches: 1,
detailFetches: 500,
githubOwnerFetches: 0,
githubOwnerIdsReused: 2,
githubOwnerProofProvenance: "stored-authenticated-staging-live",
},
};
});
vi.stubGlobal(
"fetch",
vi.fn(async (_url: string, init: RequestInit) => {
if (init.method === "GET") {
return new Response(
JSON.stringify({
deploymentName: "academic-chihuahua-392",
buildSha: "source-sha",
control: {
mode: "staging-live",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
maxEntriesPerBatch: 100,
maxWritesPerBatch: 100,
publicVisibilityEnabled: false,
},
}),
);
}
const body = JSON.parse(String(init.body)) as { operation: string; cursor?: number };
if (body.operation === "start") {
return new Response(JSON.stringify({ runId: "skillsShCatalogRuns:test" }));
}
if (body.operation === "resolve-owners") {
return new Response(
JSON.stringify({
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live",
fetches: 0,
reused: 2,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
}),
);
}
if (body.operation === "batch") {
return new Response(
JSON.stringify({
status: body.cursor === 450 ? "completed" : "running",
cursor: (body.cursor ?? 0) + 50,
}),
);
}
if (body.operation === "admit") {
return new Response(JSON.stringify({ requested: 1, admitted: 1, skipped: 0 }));
}
return new Response(JSON.stringify({ error: "unsupported_operation" }), { status: 400 });
}),
);
});
afterEach(() => {
vi.unstubAllGlobals();
});
it("resolves owners through Convex and never exposes the request OIDC token", async () => {
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(200);
const body = await response.json();
expect(body).toMatchObject({
ok: true,
source: {
selection: { rows: 500 },
fetches: {
skillsShFetches: 502,
githubOwnerFetches: 0,
githubOwnerIdsReused: 2,
githubOwnerProofProvenance: "stored-authenticated-staging-live",
},
},
convex: {
firstRun: { runId: "skillsShCatalogRuns:test" },
identicalRerun: { runId: "skillsShCatalogRuns:test" },
admission: { admitted: 1 },
},
controls: { publicVisibilityEnabled: false, schedulesEnabled: false },
});
expect(JSON.stringify(body)).not.toContain("request-oidc-token");
const fetchMock = vi.mocked(fetch);
expect(fetchMock).toHaveBeenCalledTimes(25);
for (const [, init] of fetchMock.mock.calls) {
expect(init?.headers).toMatchObject({ Authorization: "Bearer operator-token" });
expect(JSON.stringify(init)).not.toContain("request-oidc-token");
}
expect(
fetchMock.mock.calls.some(([, init]) => {
const body = init?.body ? JSON.parse(String(init.body)) : null;
return body?.operation === "admit";
}),
).toBe(true);
});
it("is a hard 404 outside the exact Test runtime", async () => {
sourcePolicyMock.mockReturnValue({
allowed: false,
environment: "production",
reason: "disabled",
});
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(404);
expect(captureSnapshotMock).not.toHaveBeenCalled();
expect(fetch).not.toHaveBeenCalled();
});
it.each([
{ body: [], label: "array body" },
{ body: { allowlist: {} }, label: "non-array allowlist" },
{ body: { allowlist: [null] }, label: "non-string allowlist member" },
{ body: { reason: 42 }, label: "non-string reason" },
])("rejects a malformed $label before calling any backend", async ({ body }) => {
readBodyMock.mockResolvedValue(body);
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(400);
expect(await response.json()).toEqual({ error: "invalid_request_body" });
expect(captureSnapshotMock).not.toHaveBeenCalled();
expect(fetch).not.toHaveBeenCalled();
});
it("sizes persistence batches from the active Convex row and write budgets", async () => {
readBodyMock.mockResolvedValue({
allowlist: ["nvidia/skills/aiq-deploy"],
reason: "bounded Test proof",
});
const batchSizes: number[] = [];
vi.stubGlobal(
"fetch",
vi.fn(async (_url: string, init: RequestInit) => {
if (init.method === "GET") {
return new Response(
JSON.stringify({
deploymentName: "academic-chihuahua-392",
buildSha: "source-sha",
control: {
mode: "staging-live",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
maxEntriesPerRun: 500,
maxEntriesPerBatch: 20,
maxWritesPerBatch: 30,
publicVisibilityEnabled: false,
},
}),
);
}
const body = JSON.parse(String(init.body)) as {
operation: string;
cursor?: number;
rows?: unknown[];
};
if (body.operation === "start") {
return new Response(JSON.stringify({ runId: "skillsShCatalogRuns:test" }));
}
if (body.operation === "resolve-owners") {
return new Response(
JSON.stringify({
authentication: "clawhub-github-authenticated",
provenance: "stored-authenticated-staging-live",
fetches: 0,
reused: 2,
owners: [
{ owner: "anthropics", login: "anthropics", id: 76_263_028 },
{ owner: "nvidia", login: "nvidia", id: 1_728_152 },
],
}),
);
}
if (body.operation === "batch") {
batchSizes.push(body.rows?.length ?? 0);
return new Response(
JSON.stringify({
status:
(body.cursor ?? 0) + (body.rows?.length ?? 0) >= 500 ? "completed" : "running",
cursor: (body.cursor ?? 0) + (body.rows?.length ?? 0),
}),
);
}
if (body.operation === "admit") {
return new Response(JSON.stringify({ requested: 1, admitted: 1, skipped: 0 }));
}
return new Response(JSON.stringify({ error: "unsupported_operation" }), { status: 400 });
}),
);
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({
controls: { batchSize: 15 },
});
expect(batchSizes).toHaveLength(68);
expect(Math.max(...batchSizes)).toBe(15);
expect(batchSizes.reduce((sum, size) => sum + size, 0)).toBe(1_000);
});
it.each([
{ status: "running", cursor: 500 },
{ status: "completed", cursor: 499 },
])("fails closed when a snapshot run ends at $status with cursor $cursor", async (terminal) => {
const baseFetch = vi.mocked(fetch);
vi.stubGlobal(
"fetch",
vi.fn(async (url: string, init: RequestInit) => {
const body = init.body ? (JSON.parse(String(init.body)) as { operation?: string }) : null;
if (body?.operation === "batch") {
return new Response(JSON.stringify(terminal));
}
return await baseFetch(url, init);
}),
);
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(502);
expect(await response.json()).toMatchObject({
error: "skills_sh_catalog_test_failed",
message: expect.stringContaining("did not complete all 500 rows"),
});
});
it("fails closed when any allowlisted real scan is skipped", async () => {
const baseFetch = vi.mocked(fetch);
vi.stubGlobal(
"fetch",
vi.fn(async (url: string, init: RequestInit) => {
const body = init.body ? (JSON.parse(String(init.body)) as { operation?: string }) : null;
if (body?.operation === "admit") {
return new Response(JSON.stringify({ requested: 1, admitted: 0, skipped: 1 }));
}
return await baseFetch(url, init);
}),
);
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(502);
expect(await response.json()).toMatchObject({
error: "skills_sh_catalog_test_failed",
message: expect.stringContaining("did not admit the complete allowlist"),
});
});
it("uses the normalized deduplicated allowlist for capture, admission, and accounting", async () => {
readBodyMock.mockResolvedValue({
allowlist: [
" NVIDIA/SKILLS/AIQ-DEPLOY ",
"",
"nvidia/skills/aiq-deploy",
" nvidia/skills/aiq-deploy ",
],
});
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(200);
expect(captureSnapshotMock).toHaveBeenCalledWith(
expect.objectContaining({
admitExternalIds: ["nvidia/skills/aiq-deploy"],
}),
);
const admitCall = vi.mocked(fetch).mock.calls.find(([, init]) => {
const body = init?.body ? JSON.parse(String(init.body)) : null;
return body?.operation === "admit";
});
expect(JSON.parse(String(admitCall?.[1]?.body))).toMatchObject({
externalIds: ["nvidia/skills/aiq-deploy"],
});
expect(await response.json()).toMatchObject({
convex: { admission: { requested: 1, admitted: 1, skipped: 0 } },
});
});
});